home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 February: Tool Chest / Dev.CD Feb 99 TC.toast / What's New? / Development Kits / Mac OS USB v1.1f3 DDK / Examples / USBSampleStorageDriver / StorageClassUTFunctions.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-12-14  |  83.0 KB  |  2,727 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        StorageClassUTFunctions.c
  3.  
  4.     Contains:    All device specific functions
  5.  
  6.     Version:    1.1
  7.  
  8.     Copyright:    © 1998 by Apple Computer, Inc., all rights reserved.
  9.  
  10. */
  11.  
  12. #include <Disks.h>
  13. #include <DriverGestalt.h>
  14. #include <Gestalt.h>
  15. #include <NameRegistry.h>
  16. #include <Power.h>
  17. #include <Resources.h>
  18. #include <Strings.h>
  19.  
  20. #include "SampleStorageDriverAPI.h"
  21. #include "SampleStorageVersion.h"
  22. #include "StorageClassUTFunctions.h"
  23. #include "StorageClassUTDriver.h"
  24. #include "StorageClassUTDriverIcons.h"
  25.  
  26. enum 
  27. {
  28.     kReadWriteRetryCount = 1,
  29.     kFormatRequestSenseRetryCount = 5
  30. };
  31.  
  32. enum {
  33. //    kCDBSize = 12,
  34.     kSenseDataSize = 18
  35. };
  36.  
  37.  
  38. enum {
  39.     kCmdFormat                     = 0x04,
  40.     kCmdInquiry                 = 0x12,
  41.     kCmdModeSelect                 = 0x55,
  42.     kCmdModeSense                 = 0x5A,
  43.     kCmdPreventAllowRemoval     = 0x1E,
  44.     kCmdRead                     = 0x28,
  45.     kCmdReadCapacity             = 0x25,
  46.     kCmdReadFormatCapacities     = 0x23,
  47.     kCmdRequestSense             = 0x03,
  48.     kCmdRezero                     = 0x01,
  49.     kCmdSeek                     = 0x2B,
  50.     kCmdSendDiagnostic             = 0x1D,
  51.     kCmdStartStopUnit             = 0x1B,
  52.     kCmdTestUnitReady             = 0x00,
  53.     kCmdWrite                     = 0x2A,
  54.     kCmdWriteVerify             = 0x2E,
  55.     kCmdVerify                     = 0x2F
  56. };
  57.  
  58. // The structure for getting the media capacity from the device    
  59. struct ReadCapacityData 
  60. {
  61.     UInt32        lastLogicalBlock;        // Last valid LBA
  62.     UInt32        blockLength;            // Block length in bytes
  63. };
  64. typedef struct ReadCapacityData     ReadCapacityData, *ReadCapacityDataPtr;
  65.  
  66. // This structure defines each volume (partition) on a physical drive (media)
  67. struct VolumeRec                        // This is needed to support multiple partitions
  68. {
  69.     DrvSts2            driveStatus;        // drive status info & queue element
  70.     UInt16            vRefNum;            // drive number for this volume
  71.     Boolean            mountthispart;        // mount this volume indicator
  72.     Boolean            partmounted;        // volume mounted indicator
  73.     UInt32            partitionNo;        // the partition number for the volume
  74.     UInt32            partoffset;            // phys. offset of data partition
  75.     UInt32            curoffset;            // 0 = physical mapping, else 'partoffset'
  76.     UInt32            partblks;            // number of blocks in the partition
  77.     Ptr                nextVol;            // Link to next volume in the list
  78.     Ptr                drivePtr;            // Pointer to the owning drive's record
  79.     Ptr                mediaIconPtr;        // Pointer to the media icon for this volume
  80. };
  81. typedef    struct VolumeRec     VolumeRec, *VolumeRecPtr;
  82.  
  83. // This structure defines each physical drive…
  84. struct DriveRec
  85. {
  86.     UInt32            capacity;            // Drive capacity in blocks
  87.     UInt32            blockSize;
  88.     UInt16            numVolumes;            // Number of partitions in the drive
  89.     VolumeRecPtr    nextVol;            // Pointer to drive's volume list    
  90. };
  91. typedef struct DriveRec     DriveRec, *DriveRecPtr;
  92.  
  93. // Our Sleep Queue structure.  The driver installs a sleep queue proc to find
  94. // out when the machine goes into sleep or doze mode.  When a sleep or doze
  95. // notification is received, the driver will stop polling for media until a wake
  96. // notification is received.    
  97. struct OurSleepQRec
  98. {
  99.     SleepQRec    theSleepQRec;
  100.     Boolean        isInSleep;
  101. };
  102. typedef struct OurSleepQRec    OurSleepQRec, *OurSleepQRecPtr;
  103.  
  104. // The Drive Request PB structure contains all information need to perform
  105. // a device request
  106. struct DriveRequestPB
  107. {
  108.     StorageExecuteCommandPB        executePB;
  109.     ParmBlkPtr                    theIOPB;
  110.     IOCommandID                 ioCommandID;
  111.     IOCommandKind                 ioCommandKind;
  112.     OSStatus                    status;
  113.     UInt16                        retryCount;
  114.     Boolean                        doWrite;
  115. };
  116. typedef struct DriveRequestPB    DriveRequestPB, *DriveRequestPBPtr;
  117.  
  118. // The driver's global data structure
  119. struct UTDriverGlobals
  120. {
  121.     DriveRequestPB                drivePB;
  122.  
  123.     // Global Control/Status fields for the driver
  124.     Boolean                        isFloppy;
  125.     Boolean                        isWriteProtected;
  126.     Boolean                        doInternalReadWrite;
  127.     Boolean                        diskInDrive;
  128.     UInt8                        currentExecutionState;
  129.     
  130.     SInt16                        drvrRefNum;            // Our driver reference number
  131.     ReadCapacityData            getCapacity;
  132.     UInt8                        sense[40];
  133.     DriveRec                    theDrive;
  134.     VolumeRec                    theVolume;
  135.     OurSleepQRec                theSleepQRec;
  136.     Str32                        DriveInfoString;
  137. };
  138. typedef struct UTDriverGlobals    UTDriverGlobals;
  139.  
  140. // The values for the states of the state machines
  141. enum
  142. {
  143.     // Mount State Machine
  144.     kMountStartState = 1,
  145.     kMountTURDoneState,
  146.     kMountRequestSenseDoneState,
  147.     kMountGetGeometryDoneState,
  148.     kMountCheckGetGeometryErrorDone,
  149.     kMountReadPossibleCapacitiesDone,
  150.     kMountCheckWriteProtectDoneState,
  151.     kMountPreventRemovalDoneState,
  152.  
  153.     // Eject State Machine
  154.     kEjectStartState,
  155.     kEjectAllowRemovalDone,
  156.     kEjectCartridgeDone,
  157.  
  158.     // Format State Machine
  159.     kFormatStartState,
  160.     kFormatDoneState,
  161.     kFormatWaitDoneState,
  162.     kFormatRequestSenseDoneState,
  163.     kFormatGetGeometryDoneState,
  164.     kFormatCheckWriteProtectDoneState,
  165.     kFormatPreventRemovalDoneState
  166. };
  167.  
  168. /* Driver Control/Status Codes for Startup Disk Control Panel */
  169. enum
  170. {
  171.     kcsSetBootPartitionCode            = 44,        // Set startup partition Control Call
  172.     kcsGetBootPartitionStatus        = 44        // Get startup partition Status Call
  173. };
  174.  
  175. // Prototypes for the Sleep Queue related functions
  176. static void             InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec);    
  177. static void             RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec);    
  178. static long             SleepNotification(long message, SleepQRecPtr sleepRec);
  179.  
  180. // Prototypes for the cartridge mounting related functions
  181. static OSStatus         MountSecondaryInterrupt( void *p1, void *p2);
  182. static void             MountTheCartridge( void *theCurrentPB );
  183.  
  184. // Prototypes for the cartridge ejecting related functions
  185. static void             EjectTheCartridge( void *theCurrentPB );
  186.  
  187. // Prototypes for the floppy formatting related functions
  188. static OSStatus         FormatCompletionInterrupt( void *p1, void *p2);
  189. static void             FormatTheCartridge( void *theCurrentPB );
  190.  
  191. // ATAPI/SCSI-2 Device Commands
  192. static OSStatus         CheckWriteProtect(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  193. static OSStatus         TUR(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  194. static OSStatus         GetMediaGeometry(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  195. static OSStatus         ReadFormatCapacity(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  196. static OSStatus            RequestSense(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  197. static OSStatus         FormatFloppyCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  198. static OSStatus         EjectCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  199. static OSStatus         PreventAllowRemoval(UTDriverGlobals *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion);
  200.  
  201. // Prototypes for the Read/Write functions and completion routines
  202. static OSStatus         DoReadWriteCommand( UTDriverGlobals *theDriverPB, Boolean doWrite);
  203. static OSStatus         ReadWriteBlock( UTDriverGlobals *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync);
  204. static void             ReadWriteCompletion( void *theDriverPB );
  205. static void             WriteRequestSenseCompletion( void *theDriverPB );
  206. static void             RequestSenseOnErrorCompletion( void *theDriverPB );
  207.  
  208. // Prototypes for the Drive Queue and Volume Queue related functions
  209. static void             InstallVolumes(DriveRecPtr theDrive, Boolean mountVols);
  210. static void                RemoveVolume(DriveRecPtr drvRec, UInt16 volRef);
  211. static VolumeRecPtr        GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum);
  212. static VCB*                MountedVolOfDrive(DriveRecPtr drive);
  213. static SInt16            NextQDrive();
  214. static VolumeRecPtr        CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset);
  215. static void             RemoveDrive(DriveRecPtr theDrivePtr);
  216. static void                UpdateQ(SInt16 qDrive, SInt32 newSize);
  217. static SInt32            NextPartitionID(DriveRecPtr drive);
  218. static Boolean             InstallDrive(DriveRecPtr drive, Boolean mountVols);
  219. static OSErr             MountVolumes( DriveRecPtr drive );
  220.  
  221. // This is to workaround a bug in the PowerPC native version of the AddDrive
  222. // call in systems before 8.5, where one needs to be added to the desired
  223. // drive number before calling AddDrive.
  224. // This function will check the system and pass the appropriate value to AddDrive
  225. static void             NativeAddDrive(SInt16 drvrRefNum, UInt16 driveNumber, DrvQElPtr drvQEl);
  226.  
  227. //----------------------------------------------------------------------------------
  228. //                                    Globals
  229. //----------------------------------------------------------------------------------
  230.  
  231. static DriverLocationIcon                gDriveIcon;                            // static structure for control calls
  232. static StorageClassDispatchTablePtr        gItsTheDispatchTable = nil;            // The class's dispatch table
  233. static TimerID                            gInterruptTimer = 0;
  234. static UTDriverGlobals                    gTheUTGlobals;
  235.  
  236.  
  237. //----------------------------------------------------------------------------------
  238. //                                    Driver calls
  239. //----------------------------------------------------------------------------------
  240.  
  241. //    Always run at task level, can allocate and move memory.
  242. OSStatus DriverInitializeCmd (    AddressSpaceID        addressSpaceID,
  243.                                 DriverInitInfoPtr    initialInfo)
  244. {
  245. #pragma unused (addressSpaceID, initialInfo)
  246.     
  247.     OSStatus    err = noErr;
  248.  
  249.     IfDebugging("\pInitialize Driver");    
  250.     gTheUTGlobals.drvrRefNum = initialInfo->refNum;
  251.     gTheUTGlobals.drivePB.theIOPB = nil;
  252.     gTheUTGlobals.currentExecutionState = kMountStartState;
  253.     gTheUTGlobals.doInternalReadWrite = false;
  254.     gTheUTGlobals.diskInDrive = false;
  255.     
  256.     gTheUTGlobals.isFloppy = false;
  257.  
  258.     // Set up our color icon family
  259.     BuildMediaIconFamily();
  260.  
  261.     // Clear out the Drive Record
  262.     BlockZero((Ptr) &gTheUTGlobals.theDrive, sizeof(DriveRec));
  263.     
  264.     // Clear out the Volume Record
  265.     BlockZero((Ptr) &gTheUTGlobals.theVolume, sizeof(VolumeRec));
  266.     
  267.     // Build the Drive Info string returned in Status/Control calls
  268.     {
  269.         Str32 tempStr;
  270.         
  271.         PStrCopy( gTheUTGlobals.DriveInfoString, "\pUSB (v");
  272.         CStrToPStr(tempStr, kStorageStringVersShort); // driver version
  273.         PStrCat(gTheUTGlobals.DriveInfoString, tempStr); // driver version
  274.         PStrCat(gTheUTGlobals.DriveInfoString, "\p)");
  275.     }
  276.  
  277.     BlockZero((Ptr) &gTheUTGlobals.theSleepQRec, sizeof(OurSleepQRec));
  278.     InstallInSleepQueue( &gTheUTGlobals.theSleepQRec );
  279.     return (err);
  280. }
  281.  
  282. //    Always run at task level, can allocate and move memory.
  283. OSStatus DriverFinalizeCmd (DriverFinalInfoPtr finalInfo)
  284. {
  285. #pragma unused (finalInfo)
  286.     OSStatus    err = noErr;
  287.  
  288.     DestroyMediaIconFamily();
  289.     RemoveFromSleepQueue( &gTheUTGlobals.theSleepQRec );    
  290.     
  291.     return (err);
  292. }
  293.  
  294. //    Always run at task level, can allocate and move memory.
  295. OSStatus DriverSupersededCmd (DriverSupersededInfoPtr supersededInfo)
  296. {
  297. #pragma unused (supersededInfo)
  298.  
  299.     OSErr    err = noErr;
  300.  
  301.     return (err);
  302. }
  303.  
  304. //    Always run at task level, can allocate and move memory.
  305. OSStatus DriverReplaceCmd (    AddressSpaceID            addressSpaceID,
  306.                             DriverReplaceInfoPtr    replaceInfo)
  307. {
  308. #pragma unused (addressSpaceID, replaceInfo)
  309.     OSStatus    err = noErr;
  310.  
  311.     return (err);
  312. }
  313.  
  314. //    Always run at task level, can allocate and move memory.
  315. OSStatus DriverOpenCmd ( AddressSpaceID    addressSpaceID, ParmBlkPtr pb)
  316. {
  317. #pragma unused (addressSpaceID, pb)
  318.     OSStatus    err         = noErr;
  319.  
  320.     // Let the world know we can do DriverGestalt calls
  321.     DriverGestaltOn(gTheUTGlobals.drvrRefNum);
  322.     
  323.     return(err);
  324. }
  325.  
  326. //    Always run at task level, can allocate and move memory.
  327. OSStatus DriverCloseCmd (ParmBlkPtr pb)
  328. {
  329. #pragma unused (pb)
  330.     OSStatus        err = noErr;
  331.  
  332.     gTheUTGlobals.diskInDrive = false;
  333.  
  334.     // If an interrupt timer is set, cancel it!
  335.     if( gInterruptTimer != 0 )
  336.     {
  337.         AbsoluteTime    timeLeft;
  338.         
  339.         // Cancel any pending timers
  340.         CancelTimer( gInterruptTimer, &timeLeft);
  341.         gInterruptTimer = 0;
  342.     }
  343.  
  344.     // If a command is pending, we should cancel it.
  345.     // Since we currently have no way of informing the Class driver to abort,
  346.     // we will wait for the class driver to tell us the command has finished.
  347.     while(    gTheUTGlobals.drivePB.theIOPB != nil );
  348.     
  349.     // Dequeue all drive volumes 
  350.     RemoveDrive(&gTheUTGlobals.theDrive);
  351.     
  352.     if(gItsTheDispatchTable != nil)
  353.     {
  354.         (void) ((gItsTheDispatchTable)->pStorageControl)(kControlEnableRemoval, nil);
  355.     }
  356.     
  357.     // Let the world know we can no longer doDriverGestalt calls
  358.     DriverGestaltOff(gTheUTGlobals.drvrRefNum);
  359.     
  360.     return (err);
  361. }
  362.  
  363. //    May run at interrupt level, CANNOT allocate or move memory.
  364. OSStatus DriverControlCmd (    AddressSpaceID    addressSpaceID,
  365.                             IOCommandID        ioCommandID,
  366.                             IOCommandKind    ioCommandKind,
  367.                             ParmBlkPtr        pb)
  368. {
  369. #pragma unused ( addressSpaceID )
  370.     OSStatus        err            = noErr;
  371.     CntrlParamPtr    pbPtr;
  372.     DriveRecPtr        drive         = &gTheUTGlobals.theDrive;
  373.  
  374.     BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  375.     
  376.     gTheUTGlobals.drivePB.theIOPB            = nil;
  377.     gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  378.     gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  379.     gTheUTGlobals.drivePB.retryCount         = 0;
  380.     gTheUTGlobals.drivePB.doWrite             = false;
  381.         
  382.     pbPtr = (CntrlParamPtr) pb;
  383.  
  384.     // Parse the control codes…
  385.     switch(pbPtr->csCode) 
  386.     {
  387.         case killCode:
  388.         {
  389.             // This driver does not support the killCode,
  390.             // return back -1 as per TechNote DV 17: Sony Driver
  391.             err = -1;
  392.         }
  393.         break;
  394.         
  395.         case kVerify:                        // Verify the media, this should only be called for floppies
  396.         {
  397.             if(gTheUTGlobals.theDrive.capacity == 0)
  398.             {
  399.                 err = nsDrvErr;            // no Media is inserted, return an error
  400.             }
  401.             else if( gTheUTGlobals.isFloppy == true )
  402.             {
  403.                 UInt16     diskCapacity;
  404.                 UInt16     startBlock;
  405.                 UInt16    numberBlocks = 16;
  406.                 UInt8    *blockBuffer;
  407.                 
  408.                 diskCapacity = gTheUTGlobals.theDrive.capacity;
  409.                 blockBuffer = (UInt8 *) NewPtrSysClear(numberBlocks * gTheUTGlobals.theDrive.blockSize);
  410.                 if(blockBuffer == nil)
  411.                 {
  412.                     err = verErr;
  413.                 }
  414.                 else
  415.                 {
  416.                     for (startBlock = 0; startBlock<diskCapacity; startBlock+=numberBlocks)
  417.                     {
  418.                         gTheUTGlobals.doInternalReadWrite = true;
  419.                         err = ReadWriteBlock( &gTheUTGlobals, startBlock, numberBlocks,(Ptr)  blockBuffer, false, false);
  420.                         gTheUTGlobals.doInternalReadWrite = false;
  421.     
  422.                         // an Error has occured                    
  423.                         if ( err != noErr )
  424.                         {
  425.                             // report a verify error
  426.                             err = verErr;
  427.                             break;
  428.                         }
  429.                     }
  430.                     DisposePtr((Ptr) blockBuffer);
  431.                 }
  432.             }
  433.             else
  434.             {
  435.                 err = noErr;            // For the regular cartridge, just report noErr
  436.             }
  437.         }
  438.         break;
  439.  
  440.         case kFormat:
  441.         {
  442.             if (gTheUTGlobals.isWriteProtected == true)
  443.             {
  444.                 // check for write protect
  445.                 err = wPrErr;
  446.                 break;
  447.             }
  448.             
  449.             if(gTheUTGlobals.theDrive.capacity == 0)
  450.             {
  451.                 err = nsDrvErr;            // no Media is inserted, return an error
  452.             }
  453.             else if( gTheUTGlobals.isFloppy == true )
  454.             {
  455.                 gTheUTGlobals.currentExecutionState = kFormatStartState;
  456.                 FormatTheCartridge( &gTheUTGlobals );
  457.                 err = gTheUTGlobals.drivePB.status;
  458.             }
  459.             else
  460.             {
  461.                 err = noErr;            // For the regular cartridge, just report noErr
  462.             }
  463.         }
  464.         break;
  465.  
  466.         case kEject:
  467.         {
  468.             // We get this call whenever a volume is put away (dragged to trash).  
  469.             Boolean hasMountedVolume = (MountedVolOfDrive(drive) != nil);
  470.  
  471.             if (!hasMountedVolume)                    // If no more mounted volumes…
  472.             {
  473.                 gTheUTGlobals.currentExecutionState = kEjectStartState;
  474.                  EjectTheCartridge( &gTheUTGlobals );
  475.             }
  476.             
  477.         }
  478.         break;
  479.  
  480.         case kSetTagBuffer:                    // This is a floppy specific control call
  481.         {            
  482.             err = controlErr;                // Return a controlErr, since we do not support this call
  483.         }
  484.         break;
  485.  
  486.         case kTrackCache:                    // This is a floppy specific control call
  487.         {            
  488.             err = controlErr;                // The driver does not keep an internal write cache,
  489.                                             // therefore report that we do not support this call
  490.         }
  491.         break;
  492.  
  493.         case kDriveIcon:                    // Return icon displayed during media initialization
  494.         case kMediaIcon:                    // Return icon displayed on desktop for media
  495.         {
  496.             VolumeRecPtr    vol         = nil;        // pointer to our volume record structure
  497.             
  498.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  499.             if ( !vol )                                // If we did not find a drive and volume
  500.             {
  501.                 err = nsDrvErr;                        // report drive not found error
  502.                 break;
  503.             }
  504.                 
  505.             BlockMove(vol->mediaIconPtr, &gDriveIcon.LocationIcon, sizeof(DiskIcon));
  506.  
  507.             // Copy over the DriveInfo string from the global param block        
  508.             PStrCopy(gDriveIcon.LocationString, gTheUTGlobals.DriveInfoString);
  509.  
  510.             // Finally, return the pointer to the icon in csParam
  511.             *(DriverLocationIcon**)pbPtr->csParam = &gDriveIcon;    
  512.  
  513.             err = noErr;                // clear any error from above
  514.         }
  515.         break;
  516.  
  517.         case kDriveInfo:                    // DRIVE INFO request (was from ATA maanger)
  518.         {
  519.             pbPtr->csParam[0] = 0;            // Upper Word is always 0;
  520.             pbPtr->csParam[1] = 0;            // Clear Lower Word
  521.             
  522.             if( gTheUTGlobals.isFloppy == true )
  523.             {
  524.                 // If we currently have a floppy loaded, return the following info
  525.                 pbPtr->csParam[1] =     ( 1 << 11 )     // Drive cardinality (0 - primary, 1 - secondary)
  526.                                     |    ( 0 << 10 )        // Media removability ( 0 - removable, 1 - fixed )
  527.                                     |     ( 0 << 9 )         // Interface ( 0 - floppy, 1 - SCSI )        
  528.                                     |     ( 1 << 8 )         // Location ( 0 - internal, 1 - external )
  529.                                                         // Bits 4,5,6,7 are reserved
  530.                                     |     4;                // Drive Type ( use 4 for SuperDrive for compatibility )
  531.             }
  532.             else if(gTheUTGlobals.theDrive.blockSize != 0)
  533.             {
  534.                 // If we currently have a cartridge loaded, return the following info
  535.                 pbPtr->csParam[1] =     ( 1 << 11 )     // Drive cardinality (0 - primary, 1 - secondary)
  536.                                     |    ( 0 << 10 )        // Media removability ( 0 - removable, 1 - fixed )
  537.                                     |     ( 1 << 9 )         // Interface ( 0 - floppy, 1 - SCSI )        
  538.                                     |     ( 1 << 8 )         // Location ( 0 - internal, 1 - external )
  539.                                                         // Bits 4,5,6,7 are reserved
  540.                                     |     1;                // Drive Type ( use 1 for Unknown drive (Not a floppy) )
  541.             }
  542.  
  543.         }
  544.         break;
  545.  
  546.         
  547.         case kDriverConfigureCode:
  548.         {
  549.             DriverConfigParam             *configPtr;            // local pointer to drive config structure
  550.                         
  551.             configPtr = (DriverConfigParam *) pbPtr;
  552.             switch(configPtr->driverConfigureSelector) 
  553.             {
  554.                 case kdgFlush:
  555.                 {
  556.                     // Since cache flushing differs between devices, this is not implemented in the sample driver.
  557.                     err = controlErr;
  558.                 }
  559.                 break;
  560.                 
  561.                 default:
  562.                 {
  563.                     err = controlErr;
  564.                 }
  565.                 break;
  566.             }
  567.         }
  568.         break;
  569.  
  570.         case kcsSetBootPartitionCode:                // Set Startup Partition
  571.         {
  572.             // Since there is currently no way to boot of USB,
  573.             // this control call will report that it is not supported
  574.             err = controlErr;
  575.         }
  576.         break;
  577.  
  578.         case kRegisterPartition:            // Register New Partition
  579.         {
  580.             // PC Exchange will call this function when it wants to redefine a partition.  It will
  581.             // pass in the drive queue element pointer of the partition to redefine, the
  582.             // new starting physical block offset, and the new block length.
  583.  
  584.             DrvQElPtr        theDrvQEl;                                // drive queue element pointer
  585.             VolumeRecPtr    vol         = nil;                        // pointer to our volume record structure
  586.             UInt32            *altParams;                                // local pointer to alternate control parameters
  587.     
  588.             altParams = (UInt32 *)&pbPtr->csParam[0];                // alternate parameters
  589.             
  590.             err = nsDrvErr;                                            // assume an invalid volume
  591.             theDrvQEl = (DrvQElPtr) altParams[THE_DRIVE];
  592.             if ( theDrvQEl )                                        // if valid queue element pointer
  593.             {
  594.                 vol = GetVolume(drive, theDrvQEl->dQDrive, 0);
  595.                 if ( vol )                                            // and valid volume reference
  596.                 {
  597.                     vol->partoffset = altParams[THE_PHYS_START];    // new partition offset
  598.                     vol->curoffset = vol->partoffset;                // current offset changes also
  599.                     vol->partblks = altParams[THE_PHYS_SIZE];        // new partition size
  600.                     
  601.                     UpdateQ(theDrvQEl->dQDrive, vol->partblks);        // Update drive queue capacity
  602.                     vol->partmounted = true;                        // volume will be mounted by PCX
  603.                     err = noErr;                                    // clear error
  604.                 }
  605.             }
  606.         }
  607.         break;
  608.             
  609.         case kGetADrive:                    // Get A Drive (Create New Partition)
  610.         {            
  611.             // PC Exchange calls this function to add a new partition.  The new partition's DrvQElPtr is
  612.             // returned. NOTE: If the driver handles multiple drives note that PC Exchange does not pass
  613.             // in the physical drive number on which to create the new partition.  However, the
  614.             // DrvQElPtr stored at the pointer passed in is for another partition on the drive.
  615.             UInt32            *altParams;                        // local pointer to alternate control parameters
  616.     
  617.             altParams = (UInt32 *)&pbPtr->csParam[0];        // alternate parameters
  618.         
  619.             if (!altParams[THE_VAR_QUEL])                    // verify a valid queue element handle
  620.                 err = paramErr;
  621.             else
  622.             {
  623.                 // create a new volume record and DrvQEl associated with the physical drive.
  624.                 // The new volume starts at offset 0 and has no capacity yet. By default, the
  625.                 // partition will not have a partition map entry on the media.
  626.                 VolumeRecPtr    vol         = nil;            // pointer to our volume record structure
  627.                     
  628.                 vol = CreateVolume(drive, NextPartitionID(drive), 0, 0 );
  629.                 if ( vol ) 
  630.                 {
  631.                     UInt16 volNumber;
  632.                     
  633.                     vol->vRefNum = NextQDrive();        // assign a logical drive number
  634.                     volNumber = vol->vRefNum;
  635.  
  636.                     NativeAddDrive(gTheUTGlobals.drvrRefNum, volNumber,(DrvQElPtr)  &vol->driveStatus.qLink);
  637.  
  638.                     // Return the DrvQElPtr at the location passed in…
  639.                     *((DrvQElPtr*)altParams[THE_VAR_QUEL]) = (DrvQElPtr) &vol->driveStatus.qLink;
  640.                 }
  641.                 else
  642.                     err = controlErr;
  643.             }
  644.         }
  645.         break;
  646.             
  647.         case kMediaPowerCSCode:                        // Set Power Mode
  648.         {
  649.             // The driver does not support power control modes
  650.             // report back a controlErr.
  651.             err = controlErr;
  652.         }
  653.         break;
  654.  
  655.         case 500:
  656.         {
  657.             AbsoluteTime    theWait;
  658.         
  659.             IfDebugging("\pSet Dispatch Table");
  660.             gItsTheDispatchTable = ( StorageClassDispatchTablePtr ) *((UInt32 *)(&pbPtr->csParam[0]));                // The class's dispatch table
  661.  
  662.             (void) ((gItsTheDispatchTable)->pStorageControl)(kControlDisableRemoval, nil);
  663.             theWait = DurationToAbsolute(durationSecond);
  664.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  665.             err = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  666.         }
  667.         break;
  668.  
  669.         case 21315:        // Disk copy format and copy
  670.         {
  671.             UInt16        theFormat;
  672.             UInt8        *theDiskImage;
  673.             
  674.             //SysDebugStr("\pDiskCopy");
  675.  
  676.             if( gTheUTGlobals.isWriteProtected == true)
  677.             {
  678.                 err = wPrErr;            // disk is write protected
  679.                 break;
  680.             }
  681.             
  682.             theFormat = pbPtr->csParam[0];
  683.             theDiskImage = (UInt8 *) *((UInt32 *) &pbPtr->csParam[1]);        // Extract the pointer to the data
  684.             gTheUTGlobals.doInternalReadWrite = true;
  685.             err = ReadWriteBlock( &gTheUTGlobals, 0, gTheUTGlobals.theDrive.capacity,(Ptr)  theDiskImage, true, false );
  686.             gTheUTGlobals.doInternalReadWrite = false;
  687.  
  688.             // an Error has occured                    
  689.             if ( err != noErr )
  690.             {
  691.                 // report an error
  692.                 err = paramErr;
  693.             }
  694.         }
  695.         break;
  696.         
  697.         default:
  698.         {
  699.             err = controlErr;
  700.         }
  701.         break;
  702.     }
  703.         
  704.     //SysDebugStr("\pEnd Control Call");
  705.     return(err);
  706. }
  707.  
  708. //    May run at interrupt level, CANNOT allocate or move memory.
  709.  
  710. OSStatus DriverStatusCmd (    AddressSpaceID    addressSpaceID,
  711.                             IOCommandID        ioCommandID,
  712.                             IOCommandKind    ioCommandKind,
  713.                             ParmBlkPtr        pb)
  714. {
  715. #pragma unused (addressSpaceID, ioCommandID, ioCommandKind)
  716.  
  717.     OSStatus            err            = noErr;
  718.     CntrlParamPtr         pbPtr;
  719.     DriveRecPtr            drive         = &gTheUTGlobals.theDrive;
  720.         
  721.     BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  722.     
  723.     gTheUTGlobals.drivePB.theIOPB            = nil;
  724.     gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  725.     gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  726.     gTheUTGlobals.drivePB.retryCount         = 0;
  727.     gTheUTGlobals.drivePB.doWrite             = false;
  728.  
  729.     pbPtr = (CntrlParamPtr) pb;
  730.  
  731.     // Finish processing the status call…
  732.     switch(pbPtr->csCode) 
  733.     {
  734.         case kReturnFormatList:
  735.         {
  736.             if( gTheUTGlobals.isFloppy == false )
  737.             {
  738.                 err = statusErr;
  739.             }
  740.             else
  741.             {
  742.                 typedef struct FormatList
  743.                 {
  744.                     UInt32        NumberBlock;
  745.                     UInt8        TSSValid         : 1;
  746.                     UInt8        IsCurrentFormat : 1;
  747.                     UInt8        CanFormat        : 1;
  748.                     UInt8        Density            : 1;
  749.                     UInt8        NoSides            : 4;
  750.                     UInt8        SecPerTrack;
  751.                     UInt16        NumberTracks;
  752.                 } FormatList;
  753.                 
  754.                 UInt8            returnNumber;
  755.                 UInt8            totalFormats = 2;
  756.                 FormatList        theFormats[2];
  757.                 
  758.                 // Setup the Formats
  759.                 // Double Density Floppy Disk
  760.                 theFormats[0].NumberBlock        = 1440;
  761.                 theFormats[0].TSSValid            = 1;
  762.                 theFormats[0].Density            = 0;            // 0 means single density, 1 means Double Density
  763.                 theFormats[0].NoSides            = 2;
  764.                 theFormats[0].SecPerTrack        = 9;
  765.                 theFormats[0].NumberTracks        = 80;
  766.                 
  767.                 // High Density Floppy Disk
  768.                 theFormats[1].NumberBlock        = 2880;
  769.                 theFormats[1].TSSValid            = 1;
  770.                 theFormats[1].Density            = 1;
  771.                 theFormats[1].NoSides            = 2;
  772.                 theFormats[1].SecPerTrack        = 18;
  773.                 theFormats[1].NumberTracks        = 80;
  774.  
  775.                 if( drive->capacity < 0x600 )
  776.                 {
  777.                     theFormats[0].CanFormat            = 0;        // 0 means we can format this disk as 720K
  778.                     theFormats[1].CanFormat            = 1;        // 1 means we can not format this disk as 1.44M
  779.                     theFormats[0].IsCurrentFormat    = 1;         // 1 means 720K MFM Disk is installed
  780.                     theFormats[1].IsCurrentFormat     = 0;        // 0 means 1.44M MFM Disk is not installed
  781.                 }
  782.                 else
  783.                 {
  784.                     theFormats[0].CanFormat            = 1;        // 1 means we can not format this disk as 720K
  785.                     theFormats[1].CanFormat            = 0;        // 0 means we can format this disk as 1.44M
  786.                     theFormats[0].IsCurrentFormat    = 0;         // 0 means 720K MFM Disk is not installed
  787.                     theFormats[1].IsCurrentFormat     = 1;        // 1 means 1.44M MFM Disk is installed
  788.                 }
  789.  
  790.                 returnNumber = *((UInt16 *) &pbPtr->csParam[0]);
  791.                 if( totalFormats < returnNumber )
  792.                 {
  793.                     returnNumber = totalFormats;
  794.                 }
  795.                 
  796.                 BlockCopy( (Ptr) &theFormats[0], (Ptr) (*((UInt32 *) &pbPtr->csParam[1])), sizeof(FormatList) * returnNumber);
  797.                 *((UInt16 *) &pbPtr->csParam[0]) = returnNumber;
  798.             }
  799.         }
  800.         break;
  801.     
  802.         case kDriveStatus:
  803.         {
  804.             VolumeRecPtr        vol         = nil;
  805.             
  806.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  807.             BlockMove(&vol->driveStatus, (UInt8 *) &pbPtr->csParam[0], sizeof(DrvSts));
  808.         }
  809.         break;
  810.     
  811.         case kMFMStatus:
  812.         {
  813.             if( gTheUTGlobals.isFloppy == false )
  814.             {
  815.                 err = statusErr;
  816.             }
  817.             else
  818.             {
  819.                 pbPtr->csParam[0] = -3;                            // PC Industry standard MFM (no GCR support)
  820.                 pbPtr->csParam[1] = -1;                            // MFM Disk installed
  821.                 
  822.                 if( drive->capacity < 0x600 )
  823.                 {
  824.                     pbPtr->csParam[2] = 0;                        // 720K MFM Disk installed
  825.                 }
  826.                 else
  827.                 {
  828.                     pbPtr->csParam[2] = -1;                        // 1.44M MFM Disk installed
  829.                 }
  830.                 
  831.                 pbPtr->csParam[3] = -5;                            // Generic PC Floppy Disk Controller
  832.             }
  833.         }
  834.         break;
  835.     
  836.         case kDriverGestaltCode:
  837.         {
  838.             DriverGestaltParam             *gestaltPtr;            // local pointer to drive gestalt structure
  839.             VolumeRecPtr                vol = nil;
  840.  
  841.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  842.             if (vol)
  843.                 drive = (DriveRecPtr) vol->drivePtr;            // get its drive record
  844.  
  845.             gestaltPtr = (DriverGestaltParam *) pbPtr;
  846.             switch(gestaltPtr->driverGestaltSelector) 
  847.             {
  848.                 case kdgVersion:
  849.                 {
  850.                     // Return information on the driver version
  851.                     NumVersion* numVersion;
  852.                     
  853.                     numVersion = GetDriverGestaltVersionResponse(gestaltPtr);
  854.                     
  855.                     numVersion->majorRev =            kStorageHexMajorVers;
  856.                     numVersion->minorAndBugRev =    kStorageHexMinorVers;
  857.                     numVersion->stage =                kStorageReleaseStage;
  858.                     numVersion->nonRelRev =            kStorageCurrentRelease;
  859.                 }
  860.                 break;
  861.                 
  862.                 case kdgDeviceType:
  863.                 {
  864.                     // Return the type of device--either floppy disk or removable disk
  865.                     DriverGestaltDevTResponse* deviceTypeResponse = GetDriverGestaltDevTResponse(gestaltPtr);
  866.  
  867.                     if( gTheUTGlobals.isFloppy == true )
  868.                     {
  869.                         deviceTypeResponse->deviceType = kdgFloppyType;
  870.                     }
  871.                     else
  872.                     {
  873.                         deviceTypeResponse->deviceType = kdgRemovableType;
  874.                     }
  875.                 }
  876.                 break;
  877.                 
  878.                 case kdgInterface:
  879.                 {
  880.                     // Return the interface of the drive in ioVRefNum.
  881.                     DriverGestaltIntfResponse* interfaceResponse = GetDriverGestaltIntfResponse(gestaltPtr);        
  882.  
  883.                     interfaceResponse->interfaceType = 'USB ';
  884.                 }
  885.                 break;
  886.  
  887.                 case kdgSync:
  888.                 {
  889.                     // Return true if the driver supports only synchronous behavior
  890.                     DriverGestaltSyncResponse*    syncResponse = GetDriverGestaltSyncResponse(gestaltPtr);
  891.  
  892.                     syncResponse->behavesSynchronously = false;
  893.                 }
  894.                 break;
  895.                     
  896.                 case kdgBoot:
  897.                 {
  898.                     // Return the ID of the boot device for PRAM storage
  899.                     if (!drive || !vol)
  900.                         err = nsDrvErr;
  901.                     else
  902.                     {
  903.                         // Since booting from USB is not yet supported, return statusErr.
  904.                         err = statusErr;
  905.                     }
  906.                 }
  907.                 break;
  908.  
  909.                 case kdgWide:
  910.                 {
  911.                     // Return whether driver supports large volume addressing (> 4GByte)
  912.                     // Unless our cartridges are greater than 4 GB, we don't support
  913.                     // or need to support wide block addressing
  914.                     Boolean* response = GetDriverGestaltBooleanResponse(gestaltPtr);
  915.                     
  916.                     *response = false; 
  917.                 }
  918.                 break;
  919.  
  920.                 case kdgPurge:        // Return if we can be closed and purged from memory.
  921.                 {
  922.                     DriverGestaltPurgeResponse* response = GetDriverGestaltPurgeResponse(gestaltPtr);
  923.                                         
  924.                     // Currently the driver cannot be closed or purged once installed
  925.                     response->purgePermission = kmNoCloseNoPurge;
  926.  
  927.                     response->purgeDriverPointer = (Ptr) nil;
  928.                 }
  929.                 break;
  930.                     
  931.                 case kdgSupportsSwitching:
  932.                 {
  933.                     // Return whether driver supports low power control call (csCode = 70h)
  934.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  935.                 }
  936.                 break;
  937.                     
  938.                 case kdgSupportsPowerCtl:
  939.                 {
  940.                     // Return whether driver supports low power control call (csCode = 70h)
  941.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  942.                 }
  943.                 break;
  944.                     
  945.                 case kdgAPI:
  946.                 {
  947.                     // Return whether driver supports PC-Exchange Control and Status calls
  948.                     // related to partitioning.
  949.                     DriverGestaltAPIResponse* apiResponse = GetDriverGestaltAPIResponse(gestaltPtr);
  950.                     
  951.                     apiResponse->partitionCmds = true;
  952.                 }
  953.                 break;
  954.                 
  955.                 case kdgFlush:
  956.                 {
  957.                     // Return whether driver supports the Cache flush Control call,
  958.                     // and whether the finder should tell us to flush our cache
  959.                     // Since cache flushing differs between devices, this is not implemented in the sample driver.
  960.                     DriverGestaltFlushResponse* response = GetDriverGestaltFlushResponse(gestaltPtr);
  961.  
  962.                     response->canFlush = false;
  963.                     response->needsFlush = false;
  964.                 }
  965.                 break;
  966.  
  967.                 case kdgEject:
  968.                 {
  969.                     // Return whether driver wants eject call for shutdown and restart
  970.                     // Eject on restart or shutdown
  971.                     DriverGestaltEjectResponse* response = GetDriverGestaltEjectResponse(gestaltPtr);
  972.                     response->ejectFeatures = 0L; // Clear both Dont Eject Bits, kShutDownDontEject and kRestartDontEject.
  973.                 }
  974.                 break;
  975.  
  976.                 case kdgVMOptions:
  977.                 {
  978.                     // Return whether drive can be used for Virtual Memory
  979.                     // Don't support use of media for VM
  980.                     DriverGestaltVMOptionsResponse* response = GetDriverGestaltVMOptionsResponse(gestaltPtr);
  981.                     response->vmOptions = kAllowVMNoneMask;
  982.                 }
  983.                 break;
  984.  
  985.                 case kdgMediaInfo:
  986.                 {
  987.                     // Return back specific information about our media
  988.                     DriverGestaltMediaInfoResponse* response = GetDriverGestaltMediaInfoResponse(gestaltPtr);
  989.                     //response->numberBlocks =    vol->partblks;
  990.                     response->numberBlocks =    gTheUTGlobals.theDrive.capacity;
  991.                     response->blockSize =        gTheUTGlobals.theDrive.blockSize;
  992.                     if(gTheUTGlobals.theDrive.blockSize == 0)
  993.                     {
  994.                         response->mediaType =        kMediaTypeNoMedia;
  995.                     }
  996.                     else
  997.                     {
  998.                         response->mediaType =        kMediaTypeUnknown;
  999.                     }
  1000.                 }
  1001.                 break;
  1002.  
  1003.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  1004.                 /* Disk Driver physical drive (formerly in csCode 22) in driverGestaltResponse. */
  1005.                 case kdgPhysDriveIconSuite:
  1006.  
  1007.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  1008.                 /* Disk Driver media (formerly in csCode 21) in driverGestaltResponse. */
  1009.                 case kdgMediaIconSuite:
  1010.                 {
  1011.                     // If the media is currently a floppy, return a statusErr so the system will
  1012.                     // handle the 3D Color icon information
  1013.                     if( gTheUTGlobals.isFloppy == true )
  1014.                     {
  1015.                         if(FloppyMediaIconFamily == nil )
  1016.                         {
  1017.                             err = statusErr;
  1018.                         }
  1019.                         else
  1020.                         {
  1021.                             gestaltPtr->driverGestaltResponse = (UInt32) *FloppyMediaIconFamily;
  1022.                         }
  1023.                     }
  1024.                     else
  1025.                     {
  1026.                         if(CartridgeMediaIconFamily == nil )
  1027.                         {
  1028.                             err = statusErr;
  1029.                         }
  1030.                         else
  1031.                         {
  1032.                             gestaltPtr->driverGestaltResponse = (UInt32) CartridgeMediaIconFamily;
  1033.                         }
  1034.                     }
  1035.                 }
  1036.                 break;
  1037.  
  1038.                 case kdgMediaName:
  1039.                 {
  1040.                     /* Return a pointer to a pascal string describing the Disk Driver (formerly in csCode 21) in driverGestaltResponse. */
  1041.                     gestaltPtr->driverGestaltResponse = (UInt32) &gTheUTGlobals.DriveInfoString;
  1042.                 }
  1043.                 break;
  1044.  
  1045.                 default: 
  1046.                     err = statusErr;        // unknown DriverGestalt selector
  1047.             }
  1048.         }
  1049.         break;
  1050.         
  1051.         case kcsGetBootPartitionStatus:     // Is this the boot partition?
  1052.         {
  1053.             // Since USB drives don't support booting, always report false
  1054.             err = statusErr;
  1055.         }
  1056.         break;
  1057.         
  1058.         case kGetPartInfo:
  1059.         {
  1060.             // PC Exchange will call this function to get info on the specified partition. 
  1061.             // Return the physical drive reference, the starting block offset, and 
  1062.             // the partition ID (any relative non-zero reference).
  1063.     
  1064.             UInt32            *altParams;                // alternate csParams as long words
  1065.             partInfoRecPtr     thePartInfo;
  1066.             VolumeRecPtr    vol = nil;
  1067.             
  1068.             altParams = (UInt32 *) &pbPtr->csParam[0];            // alternate parameters
  1069.             thePartInfo = (partInfoRecPtr)altParams[kPartInfoResponse];
  1070.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  1071.     
  1072.             // SCSIID is not defined for non-SCSI devices.  For now we use the LUN only
  1073.             *((UInt32 *)&thePartInfo->SCSIID) = 0;
  1074.     
  1075.             thePartInfo->physPartitionLoc = vol->partoffset;
  1076.             thePartInfo->partitionNumber = vol->partitionNo;
  1077.         }
  1078.         break;
  1079.     
  1080.         case kMediaPowerCSCode:
  1081.         {
  1082.             // Driver does not support power control modes,
  1083.             // so just return a statusErr
  1084.             err = statusErr;
  1085.         }
  1086.         break;
  1087.  
  1088.         case 17494:                            // DiskCopy version supported
  1089.         {
  1090.             pbPtr->csParam[0] = 0x0410;        // We support the Diskcopy 4.1 API
  1091.         }
  1092.         break;
  1093.         
  1094.         default:                            // Unrecognized status call
  1095.         {
  1096.             err = statusErr;
  1097.         }
  1098.         break;
  1099.     }
  1100.     //SysDebugStr("\pEnd Status Call");
  1101.     
  1102.     return(err);
  1103. }
  1104.  
  1105. //    May run at interrupt level, CANNOT allocate or move memory.
  1106.  
  1107. OSStatus DriverReadCmd (    AddressSpaceID    addressSpaceID,
  1108.                             IOCommandID        ioCommandID,
  1109.                             IOCommandKind    ioCommandKind,
  1110.                             ParmBlkPtr        pb)
  1111. {
  1112. #pragma unused ( addressSpaceID )
  1113.     OSStatus        err = ioErr;
  1114.  
  1115.     if( gTheUTGlobals.diskInDrive == false)
  1116.     {
  1117.         err = ioErr;
  1118.     }
  1119.     else
  1120.     {
  1121.         //SysDebugStr("\pRead Call;g");
  1122.         // Clear out the Drive request PB
  1123.         BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  1124.         
  1125.         gTheUTGlobals.drivePB.theIOPB            = pb;
  1126.         gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  1127.         gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  1128.         gTheUTGlobals.drivePB.retryCount         = 0;
  1129.         gTheUTGlobals.drivePB.doWrite             = false;
  1130.     
  1131.         err = DoReadWriteCommand( &gTheUTGlobals, false);
  1132.     }
  1133.     return(err);
  1134. }
  1135.  
  1136. //    May run at interrupt level, CANNOT allocate or move memory.
  1137.  
  1138. OSStatus DriverWriteCmd (    AddressSpaceID    addressSpaceID,
  1139.                             IOCommandID        ioCommandID,
  1140.                             IOCommandKind    ioCommandKind,
  1141.                             ParmBlkPtr        pb)
  1142. {
  1143. #pragma unused ( addressSpaceID )
  1144.     OSStatus        err = ioErr;
  1145.  
  1146.     if( gTheUTGlobals.diskInDrive == false )
  1147.     {
  1148.         err = ioErr;
  1149.     }
  1150.     else
  1151.     {
  1152.         //SysDebugStr("\pWrite Call");
  1153.         // Clear out the Drive request PB
  1154.         BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  1155.     
  1156.         gTheUTGlobals.drivePB.theIOPB            = pb;
  1157.         gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  1158.         gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  1159.         gTheUTGlobals.drivePB.retryCount         = 0;
  1160.         gTheUTGlobals.drivePB.doWrite             = true;
  1161.             
  1162.         err = DoReadWriteCommand( &gTheUTGlobals, true);
  1163.     }
  1164.     
  1165.     return(err);
  1166. }
  1167.  
  1168. //    May run at interrupt level, CANNOT allocate or move memory.
  1169.  
  1170. OSStatus DriverKillIOCmd (ParmBlkPtr pb)
  1171. {
  1172. #pragma unused (pb)
  1173.     OSStatus    err = noErr;
  1174.  
  1175.     return (err);
  1176. }
  1177.  
  1178. void InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec)    
  1179. {
  1180.     Boolean hasPMDispatch;
  1181.     SInt32    status;
  1182.  
  1183.     // To do most power management we must have the Power Manager Dispatch routines.      
  1184.     if (Gestalt(gestaltPowerMgrAttr, &status) == 0)                                
  1185.         hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false;    
  1186.  
  1187.     if (hasPMDispatch)                                                            
  1188.     {
  1189.         // Power Manager manages a spindown timer for the internal drive.  When the
  1190.         // timer expires it calls all routines registered in the HD Queue, and then,
  1191.         // if PG&E is present (Powerbooks) it will turn off power to the drive.
  1192.         // If PG&E is not present (Desktops) Power Manager can't turn off power and
  1193.         // expects one of the queue routines to reduce drive power instead. 
  1194.         //  Therefore, if we manage the internal drive we should be in the HD Queue.
  1195.  
  1196.             ourSleepQRec->theSleepQRec.sleepQLink = nil;                                
  1197.             ourSleepQRec->theSleepQRec.sleepQType = sleepQType;                            
  1198.             ourSleepQRec->theSleepQRec.sleepQProc = NewSleepQProc( &SleepNotification);        
  1199.             //ourSleepQRec->theSleepQRec.sleepQProc = (SleepQUPP) NewSleepQProc( &SleepNotification);        
  1200.             ourSleepQRec->theSleepQRec.sleepQFlags = 0;                // reserved    
  1201.             ourSleepQRec->isInSleep = false;    
  1202.             SleepQInstall( (SleepQRecPtr) ourSleepQRec );                            
  1203.     }                                                                            
  1204. }                                                                                
  1205.  
  1206. void RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec)    
  1207. {
  1208.     Boolean hasPMDispatch;
  1209.     SInt32    status;
  1210.  
  1211.     // To do most power management we must have the Power Manager Dispatch routines.      
  1212.     if (Gestalt(gestaltPowerMgrAttr, &status) == 0)                                
  1213.         hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false;    
  1214.  
  1215.     if (hasPMDispatch)                                                            
  1216.     {
  1217.         SleepQRemove( (SleepQRecPtr) ourSleepQRec );
  1218.     }
  1219. }                                                                                
  1220.  
  1221.  
  1222. long SleepNotification(long message, SleepQRecPtr sleepRec)
  1223. {
  1224.     OurSleepQRecPtr    ourSleepRec = (OurSleepQRecPtr) sleepRec;
  1225.     SInt32             response = noErr;                // assume we accept command
  1226.  
  1227.     switch(message)
  1228.     {
  1229.         case dozeRequest:                    // ##### Request to doze ######
  1230.         case dozeDemand:                    // ##### Going to doze now ######
  1231.         case sleepRequest:                    // ##### Request to sleep ######
  1232.         case sleepDemand:                    // ##### Going to sleep now ######
  1233.         case sleepNow:
  1234.             ourSleepRec->isInSleep = true;
  1235.             break;
  1236.  
  1237.         case sleepWakeUp:                    // ##### Wakeup from sleep ######
  1238.         case sleepRevoke:                    // ##### Someone denied sleep ######
  1239.         case dozeWakeUp:                    // ##### Wakeup from doze ######
  1240.             ourSleepRec->isInSleep = false;
  1241.             break;
  1242.     }
  1243.  
  1244.     return(response);
  1245. }
  1246.  
  1247.  
  1248. OSStatus MountSecondaryInterrupt( void *p1, void *p2)
  1249. {
  1250. #pragma unused ( p1, p2 )
  1251.     OSStatus             status;
  1252.     AbsoluteTime        theWait;
  1253.     UInt32                classDriverStatus;
  1254.  
  1255.     IfDebugging("\pMountSecondaryInterrupt");    
  1256.     gInterruptTimer = 0;
  1257.     
  1258.     if(gItsTheDispatchTable)
  1259.     {
  1260.         // Check if class driver is configured, if not setup another interrupt
  1261.         status = ((gItsTheDispatchTable)->pStorageStatus)(kStatusConfiguration, &classDriverStatus);
  1262.         if (status == noErr)
  1263.         {
  1264.             if ((classDriverStatus == kConfigureInProgress) || (gTheUTGlobals.theSleepQRec.isInSleep == true))
  1265.             {
  1266.                 theWait = DurationToAbsolute(durationSecond);
  1267.                 theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1268.                 status = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1269.                 
  1270.                 return noErr;
  1271.             } 
  1272.             else if (classDriverStatus == kConfigureComplete)
  1273.             {
  1274.                 gTheUTGlobals.currentExecutionState = kMountStartState;
  1275.                 MountTheCartridge( &gTheUTGlobals );
  1276.             }
  1277.         }
  1278.     }
  1279.     
  1280.     return noErr;
  1281. }
  1282.  
  1283.  
  1284. void MountTheCartridge( void *theCurrentPB )
  1285. {
  1286.     OSStatus         err = noErr;
  1287.     AbsoluteTime    oneSecondWait;
  1288.     DriveRec        *drive;
  1289.     UTDriverGlobals    *ourPB;
  1290.  
  1291.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1292.     drive = &ourPB->theDrive;
  1293.     oneSecondWait = DurationToAbsolute(durationSecond);
  1294.     //oneSecondWait = DurationToAbsolute(durationMillisecond * 20);
  1295.     switch (ourPB->currentExecutionState )
  1296.     {
  1297.         case kMountStartState:
  1298.         {
  1299.             ourPB->currentExecutionState = kMountTURDoneState;
  1300.             err = TUR( ourPB, &MountTheCartridge);
  1301.         }
  1302.         break;
  1303.         
  1304.         case kMountTURDoneState:
  1305.         {
  1306.             ourPB->currentExecutionState = kMountRequestSenseDoneState;
  1307.             err = RequestSense( ourPB, &MountTheCartridge);
  1308.         }
  1309.         break;
  1310.         
  1311.         case kMountRequestSenseDoneState:
  1312.         {
  1313.             if((ourPB->sense[12] == 0x00) && (ourPB->drivePB.executePB.status == noErr))
  1314.             {
  1315.                 ourPB->currentExecutionState = kMountGetGeometryDoneState;
  1316.                 ourPB->getCapacity.lastLogicalBlock = 0;
  1317.                 ourPB->getCapacity.blockLength = 0;
  1318.                 err = GetMediaGeometry( ourPB, &MountTheCartridge);
  1319.             }
  1320.             else
  1321.             {
  1322.                 ourPB->currentExecutionState = kMountStartState;
  1323.                 oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1324.                 err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1325.                 err = noErr;
  1326.             }
  1327.         }
  1328.         break;
  1329.  
  1330.         case kMountGetGeometryDoneState:
  1331.         {
  1332.             ReadCapacityData        *getGeometry;
  1333.             
  1334.             getGeometry = (ReadCapacityData *) &ourPB->getCapacity;
  1335.  
  1336.             drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in
  1337.             drive->blockSize = getGeometry->blockLength;
  1338.             
  1339.             if((drive->capacity == 0) || (drive->blockSize == 0))
  1340.             {
  1341.                 //Find out why the capacity is zero
  1342.                 ourPB->currentExecutionState = kMountCheckGetGeometryErrorDone;
  1343.                 err = RequestSense( ourPB, &MountTheCartridge);
  1344.                 break;
  1345.             }
  1346.  
  1347.             if(    drive->capacity >0x0C00)
  1348.             {
  1349.                 ourPB->isFloppy = false;
  1350.             }
  1351.             else
  1352.             {
  1353.                 ourPB->isFloppy = true;
  1354.             }
  1355.             
  1356.             ourPB->diskInDrive = true;
  1357.             
  1358.             // Check to see if media is write protected
  1359.             ourPB->currentExecutionState = kMountCheckWriteProtectDoneState;
  1360.             err = CheckWriteProtect(ourPB, &MountTheCartridge);
  1361.         }
  1362.         break;
  1363.  
  1364.         case kMountCheckGetGeometryErrorDone:
  1365.         {
  1366.             if( (ourPB->sense[12] == 0x30) && (ourPB->sense[13] == 0x01))
  1367.             {
  1368.                 // We have an unformatted cartridge, find out what format it can have
  1369.                 ourPB->currentExecutionState = kMountReadPossibleCapacitiesDone;
  1370.                 err = ReadFormatCapacity(ourPB, &MountTheCartridge);
  1371.             }
  1372.             else
  1373.             {
  1374.                 ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1375.                 oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1376.                 err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1377.             }
  1378.         }
  1379.         break;
  1380.         
  1381.         case kMountReadPossibleCapacitiesDone:
  1382.         {
  1383.             if( (ourPB->sense[3] >= 0x08) && (ourPB->sense[8] & 0x01 == 0x01))
  1384.             {
  1385.                 // this is the maximum format for this cartridge
  1386.                 drive->capacity = *((UInt32 *) &ourPB->sense[4]);
  1387.                 drive->blockSize = *((UInt16 *) &ourPB->sense[10]);
  1388.  
  1389.                 if(    drive->capacity >0x0C00)
  1390.                 {
  1391.                     ourPB->isFloppy = false;
  1392.                 }
  1393.                 else
  1394.                 {
  1395.                     ourPB->isFloppy = true;
  1396.                 }
  1397.                 
  1398.                 // Check to see if media is write protected
  1399.                 ourPB->currentExecutionState = kMountCheckWriteProtectDoneState;
  1400.                 err = CheckWriteProtect(ourPB, &MountTheCartridge);
  1401.                 break;
  1402.             }
  1403.  
  1404.             // An error occurred.  It could be:
  1405.             //        1. we didn't get a complete descriptor
  1406.             //         2. the drive doesn't recognize this media type 
  1407.             // Reset and try again. ( Should we possibly eject the media instead??)
  1408.             ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1409.             oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1410.             err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1411.         }
  1412.         break;
  1413.         
  1414.         case kMountCheckWriteProtectDoneState:
  1415.         {
  1416.             if ( ( ourPB->sense[3] & 0x80 ) != 0 )
  1417.             {
  1418.                 ourPB->isWriteProtected = true;
  1419.             }
  1420.             else
  1421.             {
  1422.                 ourPB->isWriteProtected = false;
  1423.             }
  1424.             
  1425.             ourPB->currentExecutionState = kMountPreventRemovalDoneState;
  1426.             err = PreventAllowRemoval(ourPB, true, &MountTheCartridge);
  1427.         }
  1428.         break;
  1429.         
  1430.         case kMountPreventRemovalDoneState:
  1431.         {
  1432.             // The device is now mounted, and the Media is locked in place.
  1433.             // There is nothing left for us to do, so just break.
  1434.             if (InstallDrive( drive, true ) == false)
  1435.             {
  1436.                 gTheUTGlobals.currentExecutionState = kEjectStartState;
  1437.                  EjectTheCartridge( &gTheUTGlobals );
  1438.             }
  1439.         }
  1440.         break;
  1441.     }
  1442. }
  1443.  
  1444. void EjectTheCartridge( void *theCurrentPB )
  1445. {
  1446.     OSStatus         err = noErr;
  1447.     UTDriverGlobals    *ourPB;
  1448.  
  1449.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1450.     switch (ourPB->currentExecutionState )
  1451.     {
  1452.         case kEjectStartState:
  1453.         {
  1454.             ourPB->currentExecutionState = kEjectAllowRemovalDone;
  1455.             err = PreventAllowRemoval(ourPB, false, &EjectTheCartridge); // Allow for Ejects
  1456.         }
  1457.         break;
  1458.         
  1459.         case kEjectAllowRemovalDone:
  1460.         {
  1461.             ourPB->currentExecutionState = kEjectCartridgeDone;
  1462.             EjectCartridge(&gTheUTGlobals, EjectTheCartridge);
  1463.         }
  1464.         break;
  1465.         
  1466.         case kEjectCartridgeDone:
  1467.         {
  1468.             AbsoluteTime            theWait;
  1469.             StorageExecuteCommandPB    *commandPB;
  1470.             DriveRec                *drive;
  1471.         
  1472.             drive = &ourPB->theDrive;
  1473.             RemoveDrive(drive);
  1474.             commandPB = &ourPB->drivePB.executePB;                          // use a pointer to the executePB field
  1475.             ourPB->drivePB.status = commandPB->status;
  1476.             if(commandPB->status == noErr)
  1477.             {
  1478.                 ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1479.                 theWait = DurationToAbsolute(durationSecond);
  1480.                 theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1481.                 SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1482.             }
  1483.             
  1484.             drive->capacity = 0;
  1485.             drive->blockSize = 0;
  1486.             ourPB->isFloppy = false;
  1487.             ourPB->diskInDrive = false;
  1488.         }
  1489.         break;
  1490.     }
  1491. }
  1492.  
  1493. OSStatus FormatCompletionInterrupt( void *p1, void *p2)
  1494. {
  1495. #pragma unused ( p1, p2 )
  1496.     gInterruptTimer = 0;
  1497.     gTheUTGlobals.currentExecutionState = kFormatWaitDoneState;
  1498.     FormatTheCartridge( &gTheUTGlobals );
  1499.     return noErr;
  1500. }
  1501.  
  1502. void FormatTheCartridge( void *theCurrentPB )
  1503. {
  1504.     OSStatus         err = noErr;
  1505.     AbsoluteTime    oneSecondWait;
  1506.     DriveRec        *drive;
  1507.     UTDriverGlobals    *ourPB;
  1508.  
  1509.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1510.     drive = &ourPB->theDrive;
  1511.     oneSecondWait = DurationToAbsolute(durationSecond);
  1512.  
  1513.     switch (ourPB->currentExecutionState )
  1514.     {
  1515.         case kFormatStartState:
  1516.         {
  1517.             ourPB->currentExecutionState = kFormatDoneState;
  1518.             err = FormatFloppyCartridge( ourPB, &FormatTheCartridge);
  1519.         }
  1520.         break;
  1521.         
  1522.         case kFormatDoneState:
  1523.         {
  1524.             AbsoluteTime    theWait;
  1525.             
  1526.             theWait = DurationToAbsolute(durationSecond*35);
  1527.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1528.             SetInterruptTimer( &theWait, &FormatCompletionInterrupt, nil, &gInterruptTimer);
  1529.             err = 1;
  1530.         }
  1531.         break;
  1532.         
  1533.         case kFormatWaitDoneState:
  1534.         {
  1535.             ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1536.             ourPB->drivePB.retryCount = 0;
  1537.             err = RequestSense( ourPB, &FormatTheCartridge);
  1538.         }
  1539.         break;
  1540.         
  1541.         case kFormatRequestSenseDoneState:
  1542.         {
  1543.             //SysDebugStr("\pRequest is done");
  1544.             if(ourPB->drivePB.executePB.status == noErr)
  1545.             {
  1546.                 if(ourPB->sense[12] == 0x00)
  1547.                 {
  1548.                     ourPB->currentExecutionState = kFormatGetGeometryDoneState;
  1549.                     ourPB->getCapacity.lastLogicalBlock = 0;
  1550.                     ourPB->getCapacity.blockLength = 0;
  1551.                     err = GetMediaGeometry( ourPB, &FormatTheCartridge);
  1552.                 }
  1553.                 else
  1554.                 {
  1555.                     // The format has failed, inform the OS
  1556.                     err = controlErr;
  1557.                 }
  1558.             }
  1559.             else
  1560.             {
  1561.                 if (ourPB->drivePB.retryCount < kFormatRequestSenseRetryCount)
  1562.                 {
  1563.                     ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1564.                     ourPB->drivePB.retryCount += 1;
  1565.                     err = RequestSense( ourPB, &FormatTheCartridge);
  1566.                 }
  1567.                 else
  1568.                 {
  1569.                     // The format has failed, inform the OS
  1570.                     ourPB->drivePB.retryCount = 0;
  1571.                     err = controlErr;
  1572.                 }
  1573.             }
  1574.         }
  1575.         break;
  1576.  
  1577.         case kFormatGetGeometryDoneState:
  1578.         {
  1579.             ReadCapacityData        *getGeometry;
  1580.             
  1581.             getGeometry = (ReadCapacityData *) &ourPB->getCapacity;
  1582.  
  1583.             drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in
  1584.             drive->blockSize = getGeometry->blockLength;
  1585.             
  1586.             if( (ourPB->drivePB.executePB.status != noErr) || (drive->capacity == 0) || (drive->blockSize == 0))
  1587.             {
  1588.                 // The format has failed, inform the OS
  1589.                 err = controlErr;
  1590.                 break;
  1591.             }
  1592.  
  1593.             if(    drive->capacity >0x0C00)
  1594.             {
  1595.                 ourPB->isFloppy = false;
  1596.             }
  1597.             else
  1598.             {
  1599.                 ourPB->isFloppy = true;
  1600.             }
  1601.  
  1602.             ourPB->diskInDrive = true;
  1603.         }
  1604.         break;
  1605.     }
  1606.     
  1607.     ourPB->drivePB.status = err;
  1608.     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  1609. }
  1610.  
  1611.  
  1612. OSStatus CheckWriteProtect(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1613. {
  1614.     OSStatus    status = noErr;
  1615.     
  1616.     IfDebugging("\pCheck Write Protect");
  1617.  
  1618.     if (gItsTheDispatchTable)
  1619.     {
  1620.         StorageExecuteCommandPB    *commandPB;
  1621.         
  1622.         commandPB = &theCurrentPB->drivePB.executePB;                      // use a pointer to the executePB field
  1623.         
  1624.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));            // clear out the PB we will send
  1625.         BlockZero((Ptr) &theCurrentPB->sense[0], 8);
  1626.  
  1627.         commandPB->cdb[0] =            kCmdModeSense;
  1628.         commandPB->cdb[8] =            0x08;
  1629.         commandPB->flags =             kStorageDataIn;                                    // -> Expect a data in transfer
  1630.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];                    // -> Pointer to user buffer
  1631.         commandPB->expectedCount =    8;                                                // -> Expected number of bytes to transfer
  1632.         commandPB->completionProc =    ourCompletion;                                    // -> Completion routine
  1633.         
  1634.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1635.     }
  1636.     
  1637.     return status;
  1638. }
  1639.  
  1640. OSStatus TUR(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1641. {
  1642.     OSStatus    status = noErr;
  1643.     
  1644.     IfDebugging("\pTUR");
  1645.  
  1646.     if (gItsTheDispatchTable)
  1647.     {
  1648.         StorageExecuteCommandPB    *commandPB;
  1649.         
  1650.         commandPB = &theCurrentPB->drivePB.executePB;              // use a pointer to the executePB field
  1651.         
  1652.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));    // clear out the PB we will send
  1653.     
  1654.         commandPB->flags =             kStorageNoData;    
  1655.         commandPB->completionProc =    ourCompletion;
  1656.         
  1657.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1658.     }
  1659.     
  1660.     return status;
  1661. }
  1662.  
  1663. OSStatus RequestSense(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1664. {
  1665.     OSStatus                status = noErr;
  1666.  
  1667.     if (gItsTheDispatchTable)
  1668.     {
  1669.         StorageExecuteCommandPB    *commandPB;
  1670.         
  1671.         commandPB = (StorageExecuteCommandPB *)&theCurrentPB->drivePB.executePB;              // use a pointer to the executePB field
  1672.  
  1673.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1674.         BlockZero((Ptr) &theCurrentPB->sense[0], sizeof(kSenseDataSize));
  1675.         
  1676.         commandPB->cdb[0] =            kCmdRequestSense;
  1677.         commandPB->cdb[4] =            kSenseDataSize;
  1678.         commandPB->flags =             kStorageDataIn;                            // -> Expect a data in transfer
  1679.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];            // -> Pointer to user buffer
  1680.         commandPB->expectedCount =    kSenseDataSize;                            // -> Expected number of bytes to transfer
  1681.         commandPB->completionProc =    ourCompletion;                            // -> Completion routine
  1682.         
  1683.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1684.     }
  1685.     
  1686.     return status;
  1687. }
  1688.  
  1689. OSStatus GetMediaGeometry(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1690. {
  1691.     OSStatus    status = noErr;
  1692.  
  1693.     IfDebugging("\pGetMediaGeometry");
  1694.  
  1695.     if (gItsTheDispatchTable)
  1696.     {
  1697.         ReadCapacityData        *getGeometry;
  1698.         StorageExecuteCommandPB    *commandPB;
  1699.         
  1700.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1701.         getGeometry = &theCurrentPB->getCapacity;
  1702.         
  1703.         getGeometry->lastLogicalBlock = 0;
  1704.         getGeometry->blockLength = 0;
  1705.         
  1706.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1707.  
  1708.         commandPB->cdb[0] =            kCmdReadCapacity;
  1709.         commandPB->flags =             kStorageDataIn;                            // -> Expect a data in transfer
  1710.         commandPB->userBuffer =        (Ptr) getGeometry;                        // -> Pointer to user buffer
  1711.         commandPB->expectedCount =    sizeof(ReadCapacityData);                // -> Expected number of bytes to transfer
  1712.         commandPB->completionProc =    ourCompletion;                            // -> Completion routine
  1713.         
  1714.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1715.     }
  1716.  
  1717.     return status;
  1718. }
  1719.  
  1720.  
  1721. OSStatus ReadFormatCapacity(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1722. {
  1723.     OSStatus    status = noErr;
  1724.  
  1725.     IfDebugging("\pReadFormatCapacity");
  1726.  
  1727.     if (gItsTheDispatchTable)
  1728.     {
  1729.         StorageExecuteCommandPB    *commandPB;
  1730.         
  1731.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1732.  
  1733.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1734.  
  1735.         commandPB->cdb[0] =            kCmdReadFormatCapacities;
  1736.         commandPB->cdb[7] =            0;
  1737.         commandPB->cdb[8] =            0x0C;
  1738.         commandPB->flags =             kStorageDataIn;                                    // -> Expect a data in transfer
  1739.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];                    // -> Pointer to user buffer
  1740.         commandPB->expectedCount =    0x0C;                                            // -> Expected number of bytes to transfer
  1741.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1742.         
  1743.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1744.     }
  1745.  
  1746.     return status;
  1747. }
  1748.  
  1749.  
  1750. OSStatus FormatFloppyCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1751. {
  1752.     OSStatus    status = noErr;
  1753.  
  1754.     IfDebugging("\pFormatFloppyCartridge");
  1755.  
  1756.     if (gItsTheDispatchTable)
  1757.     {
  1758.         StorageExecuteCommandPB    *commandPB;
  1759.  
  1760.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1761.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1762.         BlockZero((Ptr) &theCurrentPB->sense[0], 0x0C);
  1763.  
  1764.         // Set up the data going out
  1765.         // First set up the Defect List Header
  1766.         theCurrentPB->sense[0] = 0;
  1767.         theCurrentPB->sense[1] = 0;
  1768.         theCurrentPB->sense[2] = 0;
  1769.         theCurrentPB->sense[3] = 0x08;
  1770.  
  1771.         *((UInt32 *) &theCurrentPB->sense[4]) = theCurrentPB->theDrive.capacity;
  1772.         *((UInt16 *) &theCurrentPB->sense[10]) = theCurrentPB->theDrive.blockSize;
  1773.         
  1774.         commandPB->cdb[0] =            kCmdFormat;
  1775.         commandPB->cdb[1] =            0x17;
  1776.         commandPB->flags =             kStorageDataOut;                    // -> Expect a data out transfer
  1777.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];        // -> Pointer to user buffer
  1778.         commandPB->expectedCount =    0x0C;                                // -> Expected number of bytes to transfer
  1779.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1780.  
  1781.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1782.     }
  1783.  
  1784.     return status;
  1785. }
  1786.  
  1787.  
  1788. OSStatus EjectCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1789. {
  1790.     OSStatus    status = noErr;
  1791.  
  1792.     if (gItsTheDispatchTable)
  1793.     {
  1794.         StorageExecuteCommandPB    *commandPB;
  1795.         
  1796.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1797.  
  1798.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1799.         
  1800.         commandPB->cdb[0] =            kCmdStartStopUnit;
  1801.         commandPB->cdb[4] =            0x02;                            // Unload the Media
  1802.         commandPB->flags =             kStorageNoData;                    // -> Expect a data in transfer
  1803.         commandPB->userBuffer =        nil;                            // -> Pointer to user buffer
  1804.         commandPB->expectedCount =    0;                                // -> Expected number of bytes to transfer
  1805.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1806.         
  1807.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1808.     }
  1809.  
  1810.     return status;
  1811. }
  1812.  
  1813.  
  1814. OSStatus PreventAllowRemoval(UTDriverGlobals *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion)
  1815. {
  1816.     OSStatus    status = noErr;
  1817.  
  1818.     if (gItsTheDispatchTable)
  1819.     {
  1820.         StorageExecuteCommandPB    *commandPB;
  1821.         
  1822.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1823.  
  1824.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1825.         
  1826.         commandPB->cdb[0] =            kCmdPreventAllowRemoval;
  1827.         if ( preventRemoval == true )
  1828.         {
  1829.             commandPB->cdb[4] =            0x01;                        // Prevent Media Removal
  1830.         }
  1831.         else
  1832.         {
  1833.             commandPB->cdb[4] =            0x00;                        // Allow Media Removal
  1834.         }
  1835.         
  1836.         commandPB->flags =             kStorageNoData;                    // -> Expect a data in transfer
  1837.         commandPB->userBuffer =        nil;                            // -> Pointer to user buffer
  1838.         commandPB->expectedCount =    0;                                // -> Expected number of bytes to transfer
  1839.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1840.         
  1841.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1842.     }
  1843.  
  1844.     return status;
  1845. }
  1846.  
  1847.  
  1848. /* ---------------------Supporting Functions below this point -------------------------- */
  1849. //------------------------------------------------------------------------------
  1850. //    Function:        DRVRPrime
  1851. //    Description:    This is the ATA driver PRIME call that performs
  1852. //                    reading and writing to the device.
  1853. //                    
  1854. //    Input:            ioPB = Pointer to caller's I/O parameter block
  1855. //                    dce = Pointer to Device Control Entry (DCE)
  1856. //    Output:            A status code is returned.
  1857. //-------------------------------------------------------------------------------
  1858. OSStatus DoReadWriteCommand( UTDriverGlobals *theDriverPB, Boolean doWrite )
  1859. {
  1860.     OSStatus        err;
  1861.     UInt32            startingBlock, numBlocks;
  1862.     VolumeRecPtr    vol;
  1863.     IOParamPtr         iopb;
  1864.     DriveRecPtr     drive;
  1865.     
  1866.     drive = &theDriverPB->theDrive;
  1867.     iopb = (IOParamPtr) theDriverPB->drivePB.theIOPB;
  1868.  
  1869.     // Find the associated volume and drive records required for the request.
  1870.     vol = GetVolume(drive, iopb->ioVRefNum, 0);            // Get the volume record requested 
  1871.     
  1872.     //if (vol)                                        // if we have a volume
  1873.     //    drive = (DriveRecPtr)vol->drivePtr;            // get its drive record
  1874.     //else                // assume request if for a physical drive
  1875.     if (!vol)                                        // if we have a volume
  1876.     {
  1877.         if (drive)                                    // if physical drive matches…
  1878.         {
  1879.             vol = drive->nextVol;
  1880.             if (vol)                                // and a volume exists for it…
  1881.             {
  1882.                 if (vol->curoffset)                    // and not doing physical addressing…
  1883.                 {
  1884.                     vol = 0;                        // the volume is invalid
  1885.                 }
  1886.             }
  1887.         }
  1888.     }
  1889.     
  1890.     if (!vol || !drive)                // Abort if we don't have both drive and volume records
  1891.         return(nsDrvErr);
  1892.         
  1893.     //................................................................................
  1894.     // A usable drive and volume exists.  Continue processing the request…
  1895.  
  1896.     // Compute the starting block address for the request.  We accept
  1897.     // only the normal 32 bit address and not the new 'Large Volume (64 bit) Addressing'.
  1898.     //SysDebugStr("\pCalculate the Read.");
  1899.     startingBlock = (UInt32)((iopb->ioPosOffset)/(drive->blockSize));
  1900.     numBlocks = (iopb->ioReqCount)/(drive->blockSize);
  1901.  
  1902.     if (doWrite && vol->driveStatus.writeProt)
  1903.     {
  1904.         // check for write protect
  1905.         err = wPrErr;
  1906.     }
  1907.     else if (iopb->ioReqCount & ((drive->blockSize) - 1))
  1908.     {
  1909.         // Verify if request is a multiple of the drives blocksize
  1910.         err = paramErr;
  1911.     }
  1912.     else if ( ( startingBlock + numBlocks ) > ((vol->curoffset) ? vol->partblks : drive->capacity))
  1913.     {
  1914.         // Verify if request is within range with respect to doing physical or logical I/O.
  1915.         // Access is limited to partition range (logical I/O) if curoffset is non-zero.
  1916.         err = paramErr;
  1917.     }
  1918.     else if ((iopb->ioPosMode & rdVerify) && !doWrite)
  1919.     {
  1920.         // If Read-Verify mode, this is not efficient with disks so we simply pretend we did it.
  1921.         iopb->ioActCount = iopb->ioReqCount;
  1922.         iopb->ioPosOffset += iopb->ioActCount;
  1923.         err = noErr;
  1924.     } 
  1925.     else                                    // Do the read or write
  1926.     {
  1927.         startingBlock += vol->curoffset;            // add in the partition offset
  1928.         err = ReadWriteBlock( theDriverPB, startingBlock, numBlocks, iopb->ioBuffer, doWrite, true );
  1929.     }
  1930.  
  1931.     return err;
  1932. }
  1933.  
  1934. //------------------------------------------------------------------------------
  1935. //    Function:        ReadWriteBlock
  1936. //
  1937. //    Description:    Low level read/write block on the media with retries.
  1938. //
  1939. //    Input:            drive:            Pointer to physical drive record
  1940. //                        blockAddr:        Starting block address 
  1941. //                        numBlocks:        Number of blocks to read/write
  1942. //                        buffer:            Pointer to buffer
  1943. //                        doWrite:            1 = write, 0 = read
  1944. //
  1945. //    Output:            true if successful, false if not
  1946. //-------------------------------------------------------------------------------
  1947. OSStatus ReadWriteBlock( UTDriverGlobals *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync)
  1948. {    
  1949.     StorageExecuteCommandPBPtr     theReadWriteRequest;
  1950.     UInt32                        driveBlockSize;
  1951.     volatile OSStatus            status = noErr;
  1952.  
  1953.     IfDebugging("\p…ReadWriteBlock");
  1954.     theDriverPB->drivePB.status = noErr;
  1955.     driveBlockSize = theDriverPB->theDrive.blockSize;
  1956.     theReadWriteRequest = &(theDriverPB->drivePB.executePB);
  1957.  
  1958.     BlockZero(theReadWriteRequest, sizeof(StorageExecuteCommandPB));    // clear out the PB we will send
  1959.     theReadWriteRequest->userBuffer = buffer;                // -> Pointer to user buffer
  1960.     theReadWriteRequest->expectedCount = numBlocks*driveBlockSize;    // -> Expected number of bytes to transfer
  1961.  
  1962.     theReadWriteRequest->completionProc = (StorageClassCompletionProcPtr) ReadWriteCompletion;    // -> Completion routine
  1963.     theReadWriteRequest->actualCount = 0;                    // <- Actual number of bytes transferred
  1964.     theReadWriteRequest->status = 0;                    // <- Result of operation
  1965.  
  1966.     if(doWrite)
  1967.     {
  1968.         theReadWriteRequest->cdb[0] = kCmdWrite;
  1969.         theReadWriteRequest->flags     = kStorageDataOut;        // -> Expect a data out transfer
  1970.     }
  1971.     else
  1972.     {
  1973.         theReadWriteRequest->cdb[0] = kCmdRead;
  1974.         theReadWriteRequest->flags     = kStorageDataIn;        // -> Expect a data in transfer
  1975.     }
  1976.     
  1977.     // Set the starting block in the CDB
  1978.     theReadWriteRequest->cdb[2] = (startBlock >> 24) & 0xff;
  1979.     theReadWriteRequest->cdb[3] = (startBlock >> 16) & 0xff;
  1980.     theReadWriteRequest->cdb[4] = (startBlock >> 8) & 0xff;
  1981.     theReadWriteRequest->cdb[5] = startBlock & 0xff;
  1982.     
  1983.     // Set the Block Count in the CDB
  1984.     theReadWriteRequest->cdb[7] = (numBlocks >> 8) & 0xff;
  1985.     theReadWriteRequest->cdb[8] = numBlocks & 0xff;
  1986.  
  1987.     status = ((gItsTheDispatchTable)->pStorageExecuteCmd)( theReadWriteRequest );            
  1988.  
  1989.     theDriverPB->drivePB.status = status;
  1990.  
  1991.     if(status != 1)
  1992.     {
  1993.         theDriverPB->drivePB.theIOPB = nil;
  1994.     }
  1995.     else
  1996.     {
  1997.         if(doAsync == false)
  1998.         {
  1999.             while ( status == 1 )
  2000.             {
  2001.                 status = theDriverPB->drivePB.status;
  2002.             }
  2003.         }
  2004.     }
  2005.     
  2006.     return status;
  2007. }
  2008.  
  2009.  
  2010. void ReadWriteCompletion( void *theDriverPB )
  2011. {
  2012.     OSStatus            status;
  2013.     UTDriverGlobals        *ourPB;
  2014.     Boolean             wasWrite;
  2015.     
  2016.     //SysDebugStr("\pReadWriteCompletion;g");
  2017.     ourPB = (UTDriverGlobals *) theDriverPB;
  2018.     status = ourPB->drivePB.executePB.status;
  2019.     wasWrite = ((ourPB->drivePB.executePB.flags & kStorageDataOut) == kStorageDataOut);
  2020.     if(ourPB->doInternalReadWrite == false)
  2021.     {
  2022.         IOParamPtr         iopb;
  2023.  
  2024.         iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2025.         
  2026.         if( status == noErr )
  2027.         {
  2028.             if( wasWrite == true )
  2029.             {
  2030.                 // It was a write, do a Request sense to determine if any 
  2031.                 // errors occurred.
  2032.                 status = RequestSense( ourPB, &WriteRequestSenseCompletion);
  2033.                 
  2034.                 if( status != 1)
  2035.                 {
  2036.                     // An error occurred while trying to do the Request sense,
  2037.                     // return an ioErr to the system.
  2038.                     iopb->ioActCount = 0;
  2039.                     
  2040.                     // Set the status in the DriverPB last, this way if there is an immediate command,
  2041.                     // It won't think the command is done till after our processing.
  2042.                     ourPB->drivePB.status = ioErr;
  2043.                     
  2044.                     // Signal completion of the command to the operating system
  2045.                     ourPB->drivePB.theIOPB = nil;
  2046.                     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2047.                 }
  2048.             }
  2049.             else
  2050.             {
  2051.                 // This was a read command, and no errors occurred, finish the IO request and
  2052.                 // return to the system.
  2053.                 iopb->ioActCount = iopb->ioReqCount;
  2054.                 
  2055.                 // Set the status in the DriverPB last, this way if there is an immediate command,
  2056.                 // It won't think the command is done till after our processing.
  2057.                 ourPB->drivePB.status = noErr;
  2058.                 
  2059.                 // Signal completion of the command to the operating system
  2060.                 ourPB->drivePB.theIOPB = nil;
  2061.                 FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2062.             }
  2063.         }
  2064.         else
  2065.         {
  2066.             // An error occurred on the command, do a Request sense to be certain any
  2067.             // USB Device stalls are cleared.
  2068.             status = RequestSense( ourPB, &RequestSenseOnErrorCompletion);
  2069.             
  2070.             if( status != 1)
  2071.             {
  2072.                 // An error occurred while trying to do the Request sense,
  2073.                 // return an ioErr to the system.
  2074.                 iopb->ioActCount = 0;
  2075.                 
  2076.                 // Set the status in the DriverPB last, this way if there is an immediate command,
  2077.                 // It won't think the command is done till after our processing.
  2078.                 ourPB->drivePB.status = ioErr;
  2079.                 
  2080.                 // Signal completion of the command to the operating system
  2081.                 ourPB->drivePB.theIOPB = nil;
  2082.                 FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2083.             }
  2084.         }
  2085.     }
  2086.     else
  2087.     {
  2088.         // If this is an internal command, this is all we care about
  2089.         ourPB->drivePB.status = status;
  2090.     }
  2091.  
  2092.     IfDebugging("\p…ReadWriteBlock Done");
  2093. }
  2094.  
  2095. void WriteRequestSenseCompletion( void *theDriverPB )
  2096. {
  2097.     OSStatus        status;
  2098.     UTDriverGlobals    *ourPB;
  2099.     IOParamPtr         iopb;
  2100.     
  2101.     ourPB = (UTDriverGlobals *) theDriverPB;
  2102.     status = ourPB->drivePB.executePB.status;
  2103.     iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2104.  
  2105.     if( status == noErr )
  2106.     {
  2107.         if( ( ourPB->sense[2] & 0x0F !=0 ) && ( ourPB->sense[2] & 0x0F !=1 ))
  2108.         {
  2109.             // An error has been reported back in the sense key, return 
  2110.             // an ioErr to the system.
  2111.             iopb->ioActCount = 0;
  2112.             ourPB->drivePB.status = ioErr;
  2113.         }
  2114.         else
  2115.         {
  2116.             // No errors has been reported back in the sense key, return 
  2117.             // a noErr to the system.
  2118.             iopb->ioActCount = iopb->ioReqCount;
  2119.             ourPB->drivePB.status = noErr;
  2120.         }
  2121.     }
  2122.     else
  2123.     {
  2124.         // Errors occurred on the Request sense report an
  2125.         // ioErr back to the system
  2126.         iopb->ioActCount = 0;
  2127.         ourPB->drivePB.status = ioErr;
  2128.     }
  2129.  
  2130.     // Signal completion of the command to the operating system
  2131.     //SysDebugStr("\pWriteReqSenseFinishCommand");
  2132.     ourPB->drivePB.theIOPB = nil;
  2133.     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2134. }
  2135.  
  2136. void RequestSenseOnErrorCompletion( void *theDriverPB )
  2137. {
  2138.     UTDriverGlobals    *ourPB;
  2139.     IOParamPtr         iopb;
  2140.     
  2141.     ourPB = (UTDriverGlobals *) theDriverPB;
  2142.     iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2143.         
  2144.     // An error occurred on the initial command, and no error occurred on the RequestSense
  2145.     if( (ourPB->drivePB.retryCount < kReadWriteRetryCount) && (ourPB->drivePB.executePB.status == noErr))
  2146.     {
  2147.         OSStatus err;
  2148.         // We have not yet exceeded the retry count, so try the operation again
  2149.         // Increment our retry counter
  2150.         ourPB->drivePB.retryCount += 1;
  2151.  
  2152.         // Send the command out again
  2153.         err = DoReadWriteCommand( ourPB, ourPB->drivePB.doWrite);
  2154.         
  2155.         if ( err !=1 )
  2156.         {
  2157.             iopb->ioActCount = 0;
  2158.             ourPB->drivePB.status = ioErr;
  2159.         
  2160.             // Signal completion of the command to the operating system
  2161.             ourPB->drivePB.theIOPB = nil;
  2162.             FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2163.         }
  2164.     }
  2165.     else
  2166.     {
  2167.         // Since we only get here if an error occurred on the orignal
  2168.         // command, and the Request Sense was to clear any device stalls.
  2169.         // We will always report back an ioErr to the system.
  2170.         iopb->ioActCount = 0;
  2171.         ourPB->drivePB.status = ioErr;
  2172.     
  2173.         // Signal completion of the command to the operating system
  2174.         ourPB->drivePB.theIOPB = nil;
  2175.         FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2176.     }
  2177. }
  2178.  
  2179.  
  2180. //------------------------------------------------------------------------------
  2181. //    Function:        GetVolume
  2182. //
  2183. //    Description:    Searches for the volume record with the specified vRefNum and 
  2184. //                    returns its VolumeRecPtr.  Optionally, partitionNum
  2185. //                    can be use as search keys if vRefNum is zero.
  2186. //
  2187. //    Input:            drive:            the pointer to the DriveRec
  2188. //                    vRefNum:        the volume's reference number
  2189. //                    partitionNum:    the volume's partition number
  2190. //
  2191. //    Output:            pointer to volume record if found, nil if not found
  2192. //-------------------------------------------------------------------------------
  2193. VolumeRecPtr GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum)
  2194. {
  2195.     VolumeRecPtr     vol = nil;
  2196.     Boolean         found;
  2197.     
  2198.     // Search all volume records for one matching the search key
  2199.  
  2200.     vol = drive->nextVol;                        // first volume pointer
  2201.  
  2202.     while (vol)
  2203.     {
  2204.         found = (vRefNum) ? (vol->vRefNum == vRefNum) : (vol->partitionNo == partitionNum);
  2205.  
  2206.         if (found)
  2207.             return(vol);                        // found the partition
  2208.         else
  2209.             vol = (VolumeRecPtr)vol->nextVol;    // otherwise, get next volume pointer
  2210.     }
  2211.     
  2212.     return(vol);                                // nil if volume not found
  2213. }
  2214.  
  2215.  
  2216. //------------------------------------------------------------------------------
  2217. //    Function:        CreateVolume
  2218. //
  2219. //    Description:    This function creates a volume record for the specified drive.
  2220. //                        The volume is appended to the drive's volume queue and a logical
  2221. //                        logical drive is installed in the system drive queue.  It is 
  2222. //                        assumed the volume is not write protected, drive is installed,
  2223. //                        and the media is installed.  
  2224. //                    
  2225. //    Input:            drive:            pointer to the drive record of the volume to create
  2226. //                    partitionID:    the volume's partition ID
  2227. //                    volSize:        size of the volume in blocks
  2228. //                    volOffset:        block offset of volume on drive
  2229. //
  2230. //    Output:            Returns nil pointer if fails, else pointer to volume record
  2231. //
  2232. //    NOTE:            Assumes all inputs are valid!
  2233. //-------------------------------------------------------------------------------
  2234. VolumeRecPtr CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset )
  2235. {
  2236.     register VolumeRecPtr        vol;
  2237.     VolumeRecPtr*                volHandle;
  2238.     Boolean                        volInQueue = false;
  2239.     
  2240.     // When a volume is offlined by the system (greyed desktop icon) it is still
  2241.     // being used and its associated DrvQEl must remain.  Thus, its volume and 
  2242.     // physical drive record also must remain.  So, when new media is inserted 
  2243.     // we need to check for these structures and reuse them before creating new ones.
  2244.     // The structures can be reused since the file system verifies the volume.
  2245.  
  2246.     if( drive->nextVol == nil)
  2247.     {
  2248.         // Make sure the drive's volume queue points to the volume
  2249.         vol = &gTheUTGlobals.theVolume;
  2250.     }
  2251.     else
  2252.     {
  2253.         // Search for a volume record for the volume (partition) to be created…
  2254.         vol = GetVolume(drive, 0, partitionID);
  2255.         
  2256.         if (vol)                                                    // if record exists for this volume…
  2257.         {
  2258.             volInQueue = true;                                        // remember volume is already in queue
  2259.     
  2260.             if (vol->driveStatus.diskInPlace)                        // if volume is already online…
  2261.                 vol = nil;                                            // we shouldn't be here - fall thru
  2262.         }
  2263.         else                                                        // need to create volume record
  2264.         {
  2265.             vol = (VolumeRecPtr) PoolAllocateResident(sizeof(VolumeRec), true);    // Allocate storage for record
  2266.         }
  2267.     }
  2268.     
  2269.     if (vol)                                                        // if a volume record is valid…
  2270.     {    
  2271.         if( gTheUTGlobals.isFloppy == true)
  2272.         {
  2273.             vol->driveStatus.track             = 80;                    // Sectors on a MFM disk
  2274.             vol->driveStatus.sides             = -1;                    // -1 means double sided floppy
  2275.         }
  2276.         else
  2277.         {
  2278.             vol->driveStatus.track             = 0;                    // Only used on floppys
  2279.             vol->driveStatus.sides             = 0;                    // Only used on floppys
  2280.         }        
  2281.  
  2282.         if( gTheUTGlobals.isWriteProtected == true)
  2283.         {
  2284.             vol->driveStatus.writeProt     = 0x80;                        // disk is write protected
  2285.         }
  2286.         else
  2287.         {
  2288.             vol->driveStatus.writeProt     = 0;                        // not write protected yet
  2289.         }
  2290.         vol->driveStatus.diskInPlace     = 1;                        // Ejectable Disk
  2291.         vol->driveStatus.installed         = 1;                        // drive is installed
  2292.         vol->driveStatus.dQFSID         = 0;                        // File Manager's volume type
  2293.         if( gTheUTGlobals.isFloppy == true)
  2294.         {
  2295.             DrvSts    *theDriveStatus;
  2296.             
  2297.             theDriveStatus = (DrvSts *) &vol->driveStatus;
  2298.             theDriveStatus->qType         = 0;                        // This is a floppy drive status structure
  2299.             theDriveStatus->twoSideFmt = -1;                        // after 1st rd/wrt: 0=1 side, -1=2 side
  2300.             theDriveStatus->needsFlush = -1;                        // -1 for MacPlus drive
  2301.             theDriveStatus->diskErrs = 0;                            // soft error count
  2302.  
  2303.             vol->mediaIconPtr =    (Ptr) AppleFloppyMediaIcon;            // media icon for floppies
  2304.         }
  2305.         else
  2306.         {
  2307.             vol->driveStatus.qType             = 1;                    // both dQDrvSz and dQDrvSz2 are used
  2308.             vol->driveStatus.driveSize        = (UInt16) volSize;        // volume size in blocks
  2309.             vol->driveStatus.driveS1         = (UInt16) (volSize >> 16);
  2310.             
  2311.             vol->mediaIconPtr =    (Ptr) &CartridgeIcon;                // media icon for cartridge
  2312.         }
  2313.             
  2314.  
  2315.         vol->mountthispart =    false;                                // don't mount this volume,
  2316.         vol->partmounted =        false;                                // it's not mounted yet,
  2317.         vol->partitionNo =        partitionID;                        // save the partition ID
  2318.  
  2319.         // Save volume's block offset and set its access mode by setting curoffset to the
  2320.         // same (access is relative to partition offset if curoffset is non-zero, else physical)
  2321.         vol->curoffset =        volOffset;                            // block offset of volume
  2322.         vol->partoffset =        volOffset;                            // partition offset
  2323.         vol->partblks =            volSize;                            // save size for our use also
  2324.         vol->drivePtr =        (Ptr) drive;                            // pointer to vol's physical drive
  2325.         // If this record is not in our volume list yet, finish initializing and insert
  2326.         if (!volInQueue)                                            // If not in the volume queue…
  2327.         {
  2328.             vol->driveStatus.qLink = nil;                            // initialize system queue link
  2329.             vol->nextVol = nil;                                        // no link to next volume yet
  2330.  
  2331.             // Find the end of the drive's volume queue and link in the new volume record.
  2332.             volHandle = &(drive->nextVol);                            // point to start of drive's volume queue
  2333.             while (*volHandle)                                        // search for end of volume queue…
  2334.             {
  2335.                 volHandle = (VolumeRecPtr*)&((*volHandle)->nextVol);
  2336.             }
  2337.             
  2338.             *volHandle = vol;                                        // at end of queue, insert volume
  2339.         }
  2340.  
  2341.         drive->numVolumes++;                                        // update number of drive volumes
  2342.     }
  2343.     return(vol);
  2344. }
  2345.  
  2346.  
  2347. //------------------------------------------------------------------------------
  2348. //    Function:        InstallVolumes
  2349. //    Description:    Searches for partitions on the media and installs them as
  2350. //                        volumes for the associated drive.  Assumes the drive does
  2351. //                        not have any volumes installed yet. 
  2352. //
  2353. //    Input:            theDrive:    pointer to drive record
  2354. //                        mountVols:    true means to mount the drive's partitions
  2355. //
  2356. //-------------------------------------------------------------------------------
  2357. void InstallVolumes(DriveRecPtr theDrive, Boolean mountVols)
  2358. {
  2359.     VolumeRecPtr    vol = nil;
  2360.  
  2361.     // If the driver is going to support partitions, this is were the media should be 
  2362.     // scanned to see if there are any valid HFS or DOS partitions.
  2363.     
  2364.     // If no valid paritions are found, or partitions are not support, the following code
  2365.     // will create a volume of the entire media capacity, post a disk inserted
  2366.     // event and let the File System Manager try and figure it out.  Note we post a disk
  2367.     // inserted event rather than notifying FSM so if the media is not recognized (because
  2368.     // it's unformatted or the correct file system is not installed) the system will prompt
  2369.     // with a "This is not a Macintosh disk…" message.  If we call FSM instead, the user will
  2370.     // not be prompted when the media is unformatted.  This provides a way to format media.
  2371.  
  2372.     if (theDrive->numVolumes == 0)                            // if no partitions were found, or are not supported
  2373.     {
  2374.         IfDebugging("\ptheDrive->numVolumes == 0");
  2375.         vol = CreateVolume(theDrive, 1, theDrive->capacity, 0);
  2376.         if (vol)
  2377.         {
  2378.             vol->mountthispart = true;                        // post disk inserted event later
  2379.         }
  2380.     }
  2381.  
  2382.     // Add the remaining volumes to the drive queue…
  2383.     vol = theDrive->nextVol;                                // first volume of the drive
  2384.     while (vol)
  2385.     {
  2386.         if ((vol->mountthispart == true) || (mountVols == false))
  2387.         {
  2388.             UInt16 volNumber;
  2389.             
  2390.             vol->vRefNum = NextQDrive();                    // assign a logical drive number
  2391.             volNumber = vol->vRefNum;
  2392.  
  2393.             NativeAddDrive(gTheUTGlobals.drvrRefNum, volNumber,(DrvQElPtr)  &vol->driveStatus.qLink);
  2394.         }
  2395.         
  2396.         vol = (VolumeRecPtr)vol->nextVol;                    // point to the next volume
  2397.     }
  2398. }
  2399.  
  2400.  
  2401. //------------------------------------------------------------------------------
  2402. //    Function:        RemoveVolume
  2403. //    Description:    Deletes a volume and its record
  2404. //    Input:            drvRec:        pointer to physical drive record of volume
  2405. //                    volRef:        Volume reference
  2406. //-------------------------------------------------------------------------------
  2407. void RemoveVolume(DriveRecPtr drvRec, UInt16 volRef)
  2408. {
  2409.     VolumeRecPtr*    pvHandle = &(drvRec->nextVol);
  2410.     VolumeRecPtr    pvPtr = *pvHandle;
  2411.     
  2412.     while (pvPtr) 
  2413.     {
  2414.         if (pvPtr->vRefNum == volRef)                         // found the volume record
  2415.         {        
  2416.             Dequeue((QElemPtr) &(pvPtr->driveStatus.qLink), GetDrvQHdr());    // remove from drive queue        
  2417.             *pvHandle = (VolumeRecPtr)(pvPtr->nextVol);        // remove it from the linked list
  2418.             if (pvPtr == &gTheUTGlobals.theVolume)
  2419.             {
  2420.                 BlockZero((Ptr) &gTheUTGlobals.theVolume, sizeof(VolumeRec));
  2421.             }
  2422.             else
  2423.             {
  2424.                 PoolDeallocate( pvPtr );                    // release its memory
  2425.             }
  2426.             drvRec->numVolumes--;                            // decrement number of drive volumes
  2427.             break;
  2428.         }
  2429.         
  2430.         pvHandle = (VolumeRecPtr*)&(pvPtr->nextVol);        // save pointer to pv pointer
  2431.         pvPtr = (VolumeRecPtr)pvPtr->nextVol;                // point to next pv
  2432.     }            
  2433. }
  2434.  
  2435.  
  2436. //------------------------------------------------------------------------------
  2437. //    Function:        InstallDrive
  2438. //
  2439. //    Description:    Installs a physical drive and its volumes under the driver's
  2440. //                        control.  The driver determines if the drive is one it can
  2441. //                        manage, and if so, creates and initializes the drive's record,
  2442. //                        sets the drives operating mode and options, and mounts its
  2443. //                        partitions to the system.
  2444. //
  2445. //    Input:            drvNum:        physical drive reference
  2446. //                        mountVols:    true means to mount drive's partitions
  2447. //
  2448. //    Output:            true if successful, false if not
  2449. //-------------------------------------------------------------------------------
  2450. Boolean InstallDrive(DriveRecPtr theDrive, Boolean mountVols)
  2451. {
  2452.     // Initialize variables associated with the new drive.
  2453.     theDrive->numVolumes =        0;                            // no partitions yet
  2454.     
  2455.     //..............................................................................
  2456.     // Search for file system partitions on the media and install them as volumes
  2457.     // of this drive.  If no volumes, the drive must be considered unusable.
  2458.     InstallVolumes(theDrive, mountVols);
  2459.  
  2460.     if (theDrive->numVolumes)
  2461.     {
  2462.         OSErr theErr;
  2463.         
  2464.         theErr = MountVolumes( theDrive );
  2465.         if(theErr != noErr)
  2466.         {
  2467.             // For some reason, the disk could not be mounted,
  2468.             // We should eject and let the user decide whether to try again.
  2469.             gTheUTGlobals.currentExecutionState = kEjectStartState;
  2470.             EjectTheCartridge( &gTheUTGlobals );
  2471.         }
  2472.     }
  2473.     else                                                    // Abort if no volumes installed for drive        
  2474.     {
  2475.         return(false);
  2476.     }
  2477.  
  2478.     return(true);
  2479. }
  2480.  
  2481.  
  2482. //------------------------------------------------------------------------------
  2483. //    Function:        RemoveDrive
  2484. //    Description:    Removes a physical drive and its volumes from our control
  2485. //                    
  2486. //-------------------------------------------------------------------------------
  2487. void RemoveDrive(DriveRecPtr theDrivePtr)
  2488. {        
  2489.     VolumeRecPtr    vol;
  2490.     UInt16            vrefnum;
  2491.  
  2492.     if (theDrivePtr)                                        // If the drive exists…
  2493.     {
  2494.         while (theDrivePtr->nextVol)                        // dequeue all volumes on the drive…
  2495.         {
  2496.             vol = theDrivePtr->nextVol;                        // get volume
  2497.             vrefnum = vol->vRefNum;                            // get its refnum
  2498.             RemoveVolume(theDrivePtr, vrefnum);                // delete volume from our queue
  2499.         }
  2500.     }
  2501. }
  2502.  
  2503.  
  2504. //------------------------------------------------------------------------------
  2505. //    Function:        NextQDrive
  2506. //
  2507. //    Description:    Returns the next unused logical drive number from the system 
  2508. //                    Drive Queue.  The Drive Queue is searched starting with a
  2509. //                    logical drive number 8.
  2510. //                    
  2511. //    Input:            none
  2512. //
  2513. //    Output:            The highest Drive Queue drive number + 1
  2514. //-------------------------------------------------------------------------------
  2515. SInt16 NextQDrive( void )
  2516. {
  2517.     QHdrPtr    qhdr = GetDrvQHdr();                            // Pointer to Drive Queue 
  2518.     DrvQEl    *qel = (DrvQEl*) (qhdr->qHead);                    // Pointer to first element 
  2519.     SInt16    drv = 8;                                        // Start above built in drives 
  2520.  
  2521.     while (qel)                                             // While not end of queue, 
  2522.     {
  2523.         if (qel->dQDrive == drv)                             // if drive number used, 
  2524.         {
  2525.             drv++;                                            // bump number, and 
  2526.             qel = (DrvQEl*) (qhdr->qHead);                    // search from start 
  2527.         }
  2528.         else                                                // else next queue element 
  2529.             qel = (DrvQEl*) (qel->qLink);
  2530.     }
  2531.  
  2532.     return(drv);                                            // Return the next logical drive 
  2533. }
  2534.  
  2535.  
  2536. //------------------------------------------------------------------------------
  2537. //    Function:        FindMountedVol
  2538. //    Description:    Searches the volume queue for a mounted volume specified 
  2539. //                    by vRefNum.  If one is found, its VCB pointer is returned.
  2540. //                    
  2541. //    Input:            vRefNum:    the volume reference to search for
  2542. //
  2543. //    Output:            the VCB pointer (NULL = volume not mounted)
  2544. //-------------------------------------------------------------------------------
  2545. static VCB *FindMountedVol(SInt16 vRefNum)
  2546. {
  2547.     QHdrPtr    volQ = GetVCBQHdr();                            // VCB queue head pointer
  2548.     VCB        *theVol = (volQ) ? (VCB*)volQ->qHead : 0;        // first VCB
  2549.  
  2550.     while(theVol)
  2551.     {
  2552.         // The test for whether a volume is mounted or not is done using
  2553.         // the VCB fields vcbDrvNum and vcbDRefNum.  A volume is mounted
  2554.         // only if it is online.
  2555.         //
  2556.         //                    online            offline            ejected
  2557.         //
  2558.         //    vcbDrvNum        >0 (DrvNum)         0                 0
  2559.         //    vcbDRefNum        <0 (DRefNum)    <0 (-DrvNum)    >0 (DrvNum)
  2560.     
  2561.         if (theVol->vcbDrvNum == vRefNum)                    // if volume specified is online…
  2562.             break;                                            // volume is mounted
  2563.  
  2564.         theVol = (VCB*)theVol->qLink;                        // next VCB
  2565.     }
  2566.     
  2567.     return(theVol);
  2568. }
  2569.  
  2570. //------------------------------------------------------------------------------
  2571. //    Function:        MountedVolOfDrive
  2572. //
  2573. //    Description:    Searches the system VCB for a mounted volume on the specified 
  2574. //                    physical drive.
  2575. //                    
  2576. //    Input:            drive:    pointer to the physical drive's record
  2577. //
  2578. //    Output:            the VCB pointer (nil if no volume mounted for drive)
  2579. //-------------------------------------------------------------------------------
  2580. VCB * MountedVolOfDrive(DriveRecPtr drive)
  2581. {
  2582.     VolumeRecPtr    vol = nil;
  2583.     VCB                *vcb = nil;
  2584.     
  2585.     if (drive)
  2586.     {
  2587.         vol = drive->nextVol;                                    // first volume (logical drive) of drive
  2588.         while (vol)
  2589.         {
  2590.             vcb = FindMountedVol(vol->vRefNum);                    // check if a volume is mounted
  2591.             if (vcb)                                            // if mounted volume, stop now
  2592.             {
  2593.                 break;
  2594.             }
  2595.             else                                                // next volume of drive
  2596.             {
  2597.                 vol = (VolumeRecPtr)vol->nextVol;
  2598.             }
  2599.         }
  2600.     }
  2601.     
  2602.     return(vcb);
  2603. }
  2604.  
  2605.  
  2606. //------------------------------------------------------------------------------
  2607. //    Function:        UpdateQ
  2608. //    Description:    Updates the specified drive in the system drive queue with
  2609. //                    the specified capacity
  2610. //                    
  2611. //    Input:            qDrive:        the drive to update
  2612. //                    newSize:    the new drive capacity
  2613. //-------------------------------------------------------------------------------
  2614. void UpdateQ(SInt16 qDrive, SInt32 newSize)
  2615. {
  2616.     QHdrPtr qhdr = GetDrvQHdr();                                // Pointer to Drive Queue 
  2617.     DrvQEl *qel = (DrvQEl*) (qhdr->qHead);                        // Pointer to first element 
  2618.  
  2619.     while (qel) 
  2620.     {
  2621.         // Search until drive is found, then update its capacity
  2622.         if (qel->dQDrive == qDrive)                                // if drive number found, 
  2623.         {        
  2624.             qel->dQDrvSz2 = *(UInt16*)&newSize;                    // new capacity (hi word)
  2625.             qel->dQDrvSz = newSize;                                // low word of capacity 
  2626.             break;
  2627.         }
  2628.         else                                                    // else next queue element 
  2629.             qel = (DrvQEl*) (qel->qLink);
  2630.     }
  2631. }
  2632.  
  2633. //------------------------------------------------------------------------------
  2634. //    FUNCTION:    NextPartitionID
  2635. //    PURPOSE:    Returns the next unique partition ID for all volumes associated
  2636. //                with the specified drive.  NOTE: This function should be used only
  2637. //                when adding volumes which do not have a partition map on the media.
  2638. //                
  2639. //    INPUT:        drive:    pointer to the drive record to search for next partition ID
  2640. //
  2641. //    OUTPUT:        a unique partition ID related to the specified volume
  2642. //
  2643. //-------------------------------------------------------------------------------
  2644. SInt32 NextPartitionID(DriveRecPtr drive)
  2645. {
  2646.     VolumeRecPtr     vol = 0;
  2647.     SInt32             nextPartitionID = 1;                        // Start search with partition ID of 1
  2648.  
  2649.     if (drive)                                                    // if drive is present…
  2650.     {
  2651.         vol = (VolumeRecPtr)drive->nextVol;                        // first volume pointer
  2652.  
  2653.         while (vol)                                                // while not end of volume queue…
  2654.         {
  2655.             if (vol->partitionNo == nextPartitionID)            // if partition ID used
  2656.             {
  2657.                 nextPartitionID++;                                // bump partition ID
  2658.                 vol = drive->nextVol;                            // reset volume pointer
  2659.             }
  2660.             else
  2661.                 vol = (VolumeRecPtr)vol->nextVol;                // otherwise, point to next volume
  2662.         }
  2663.     }
  2664.     
  2665.     return(nextPartitionID);                                    // nil if volume not found
  2666. }
  2667.  
  2668.  
  2669. //------------------------------------------------------------------------------
  2670. //    Function:        MountVolumes
  2671. //    Description:    Mounts all volume for the specified drive.
  2672. //
  2673. //    Input:            DriveRecPtr drive:    drive with volumes to mount
  2674. //
  2675. //    Output:            Returns any errors that occur from PostEvent
  2676. //-------------------------------------------------------------------------------
  2677. OSErr MountVolumes( DriveRecPtr drive )
  2678. {
  2679.     VolumeRecPtr        vol;
  2680.     OSErr                mountErr = noErr;
  2681.     
  2682.     // Post a Disk Inserted event for all HFS volumes not yet mounted
  2683.     if (drive)                                                    // for each drive…
  2684.     {
  2685.         vol = drive->nextVol;                                    // first volume structure
  2686.         while (vol)                                             // for all volumes on drive…
  2687.         {                            
  2688.             if ((vol->driveStatus.diskInPlace != 0) &&            // if media in place,
  2689.                (vol->mountthispart) &&                             // and volume to be mounted,
  2690.                (!vol->partmounted))                                // and hasn't been done yet
  2691.             {
  2692.                 mountErr = PostEvent(diskEvt, vol->vRefNum);
  2693.  
  2694.                 if ( mountErr == noErr )
  2695.                 {
  2696.                     vol->partmounted = true;
  2697.                 }
  2698.             }
  2699.  
  2700.             vol = (VolumeRecPtr)vol->nextVol;                    // next per volume pointer
  2701.         }
  2702.     }
  2703.  
  2704.     return mountErr;                                            // return error if one occurred
  2705. }
  2706.  
  2707.  
  2708. // This is to workaround a bug in the PowerPC native version of the AddDrive
  2709. // call in systems before 8.5, where one needs to be added to the desired
  2710. // drive number before calling AddDrive.
  2711. // This function will check the system and pass the appropriate value to AddDrive
  2712. void NativeAddDrive(SInt16 drvrRefNum, UInt16 driveNumber, DrvQElPtr drvQEl)
  2713. {
  2714.     UInt32        gestaltResponse;
  2715.  
  2716.     // Check System version to see if we need to add one to AddDrive calls
  2717.     Gestalt    (gestaltSystemVersion,(long *) &gestaltResponse);
  2718.     if( (gestaltResponse&0xFFFF) < 0x0850 )
  2719.     {
  2720.         // We are on a system before 8.5, we need to add 1 to AddDrive calls
  2721.         driveNumber += 1;
  2722.     }
  2723.  
  2724.     AddDrive( drvrRefNum, driveNumber, drvQEl);
  2725. }
  2726.  
  2727.